[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [volume] [parte]


Capitolo 85.   USB

La sigla USB sta per Universal serial bus e rappresenta un tipo di bus in cui i vari componenti si possono collegare in una struttura che ricorda quella dell'albero. In questo tipo di bus, le comunicazioni sono controllate da un protocollo che non consente alle unità periferiche di comunicare direttamente tra di loro.

85.1   Caratteristiche generali

Il bus USB è fatto per connettere unità periferiche esterne a un elaboratore, al contrario di SCSI, che prevede la possibilità di un collegamento interno e di uno esterno. Il bus USB ha un collegamento speciale, attraverso connettori di tipo differente, a seconda che si tratti del lato rivolto verso l'unità periferica (il tipo «B»), oppure verso l'unità di controllo (il tipo «A»). La continuazione del bus avviene attraverso dei concentratori (hub), che da un lato si collegano come unità periferiche e dall'altro offrono più connettori uguali a quelli dell'unità di controllo. In particolare, l'unità di controllo viene definita anche root hub, come dire che si tratta del primo concentratore della struttura.

Figura 85.1. Porta USB di tipo «B» di un'unità periferica.

usb-connettore-apparecchio-comune

Figura 85.2. Un cavo USB, dove si possono notare i due connettori, «A» e «B», che hanno forme diverse.

usb-cavo-connettori

Figura 85.3. Connettori USB: quello in alto va collegato all'unità di controllo (tipo «A»), oppure al concentratore, mentre quello in basso va collegato all'unità periferica (tipo «B»).

usb-connettori-visione-frontale

Il bus USB consente di collegare un massimo di 127 dispositivi, oltre all'unità di controllo. In questo numero vanno contati anche i concentratori. A differenza del bus SCSI, qui i dispositivi non devono essere numerati manualmente, perché a questo provvede automaticamente il sistema.

Figura 85.4. Connettore USB «B», miniaturizzato, usato di solito per le macchine fotografiche.

connettore USB B mini

85.2   Alimentazione elettrica

Una caratteristica molto importante del bus USB è la possibilità di alimentare i dispositivi che vi si collegano, attraverso il collegamento del bus stesso. La corrente elettrica che può essere fornita in questo modo arriva normalmente a un massimo di 500 mA.

Un dispositivo USB comune può essere alimentato anche in modo indipendente, se il suo consumo è elevato; tuttavia, il fatto che l'alimentazione esterna non sia sempre necessaria, consente di ridurre il cablaggio per la connessione di componenti esterni, soprattutto quando questi sono di piccole dimensioni.

85.3   Collegamento di una porta USB

In condizioni normali, non è necessario essere a conoscenza del modo in cui i quattro terminali di una porta USB devono essere collegati da un punto di vista elettrico. Tuttavia, alcune vecchie schede madri hanno un bus USB integrato a cui si accede attraverso una o due file di cinque o di quattro piedini ciascuna, senza che esista uno standard preciso per il collegamento di questi alla porta USB di tipo «A». Si osservi la figura 85.5 a questo proposito.

Figura 85.5. Piedini per collegare due porte USB di tipo «A» su una scheda madre molto vecchia, in cui non è chiaro come siano disposti i vari terminali.

usb-scheda-madre-vecchia

Per prima cosa occorre comprendere un po' la logica delle connessioni USB, dal punto di vista fisico. La figura 85.6 schematizza una porta USB di tipo «A» su un pannello di un elaboratore.

Figura 85.6. Collegamento elettrico di una porta USB di tipo «A».

collegamento elettrico porta USB A

Si può osservare che i terminali utilizzati per l'alimentazione sono quelli più esterni; inoltre, i terminali interni per i dati, hanno una polarità invertita rispetto all'alimentazione. Con un po' di fortuna, anche i terminali che sporgono da una scheda madre dovrebbero rispettare questa logica, tenendo conto che, se esistono cinque terminali, uno va collegato alla massa esterna, in modo distinto dall'alimentazione negativa. Una disposizione tipica dei piedini di una scheda madre è questa:

  1. alimentazione +5 V;

  2. dati, terminale negativo;

  3. dati, terminale positivo;

  4. massa dell'alimentazione (0 V);

  5. massa esterna.

Un'altra possibilità comune è la seguente:

  1. alimentazione +5 V;

  2. dati, terminale negativo;

  3. non collegato (piedino assente);

  4. dati, terminale positivo;

  5. massa dell'alimentazione (0 V);

In queste condizioni, è necessario verificare almeno i terminali che sono adibiti al trasporto dell'alimentazione, attraverso uno strumento di misura. A elaboratore spento, si può verificare quali piedini risultano essere collegati direttamente alla massa; se sono due per ogni fila, quello più interno dovrebbe corrispondere alla massa di alimentazione (0 V), mentre quello più esterno dovrebbe essere collegato alla massa vera e propria. Il terminale esterno opposto di ogni fila, dovrebbe essere il positivo di alimentazione (+5 V), mentre i due terminali rimanenti dovrebbero essere quelli dei dati, con polarità alternate (vicino al positivo di alimentazione dovrebbe trovarsi il negativo dei dati; vicino al negativo di alimentazione, dovrebbe trovarsi il positivo dei dati). Se ci sono due file parallele di terminali, si può verificare che i piedini esterni riguardano l'alimentazione, perché risultano collegati assieme (i due terminali del positivo di alimentazione sono collegati tra loro, così come i due o i quattro terminali di massa). Accendendo l'elaboratore si può verificare che il terminale positivo dell'alimentazione ha una tensione di (+5 V).

85.4   Tipi di unità di controllo

Le unità di controllo USB sono fondamentalmente di tre tipi, in base alla loro compatibilità con lo standard OHCI (Open host controller interface), con lo standard UHCI (Universal host controller interface) o EHCI.

85.5   Kernel Linux

La gestione di un bus USB in un sistema GNU/Linux parte dal kernel, che deve essere stato predisposto per questo (sezione 72.2.19). In particolare, deve essere stato selezionato il tipo di unità di controllo; in pratica si deve attivare la gestione UHCI, OHCI, oppure EHCI. Eventualmente, per sapere a quale tipo appartiene la propria unità di controllo, dovrebbe essere sufficiente leggere il rapporto relativo al bus PCI (infatti, il bus USB si innesta normalmente in un bus PCI).

lspci -v[Invio]

00:07.2 USB Controller: VIA Technologies, Inc. VT82C586B USB (rev 11)
  (prog-if 00 [UHCI])
        Subsystem: Unknown device 0925:1234
        Flags: bus master, medium devsel, latency 32, IRQ 11
        I/O ports at d400 [size=32]
        Capabilities: [80] Power Management version 2

Nell'esempio si vede la sigla UHCI, che chiarisce di che tipo sia il bus USB.

85.5.1   Informazioni

Nel kernel, dopo aver attivato la gestione del bus USB, conviene attivare anche la gestione di un file system virtuale che consente poi di avere maggiori informazioni sul funzionamento del bus e dei suoi componenti. La voce relativa a questa funzionalità dovrebbe essere {Preliminary USB device filesystem}.

Quando questo file system è disponibile, conviene innestarlo nella directory /proc/bus/usb/, per rispettare le convenzioni comuni. Nel file /etc/fstab si può aggiungere una riga simile a quella seguente:

none    /proc/bus/usb   usbdevfs        defaults        0       0

In questo modo, si ottiene l'inserimento automatico delle informazioni sul bus USB, a partire dalla directory /proc/bus/usb/. In particolare, il file /proc/bus/usb/devices è ricco di informazioni.

T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
B:  Alloc= 11/900 us ( 1%), #Int=  1, #Iso=  0
D:  Ver= 1.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0000 ProdID=0000 Rev= 0.00
S:  Product=USB UHCI Root Hub
S:  SerialNumber=d400
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
I:  If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   8 Ivl=255ms
T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=03f0 ProdID=0105 Rev= 1.00
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA
I:  If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=usbscanner
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=   1 Ivl=  1ms
T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  3 Spd=12  MxCh= 0
D:  Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=07c4 ProdID=0103 Rev= 9.04
S:  Manufacturer=OnSpec Electronic, Inc.       
S:  Product=USB Disk
S:  SerialNumber=02A86999A8
C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=  0mA
I:  If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=  0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=  0ms

L'esempio mostra le informazioni ottenute dalla presenza di un'unità di controllo, uno scanner e un disco esterno. Il punto di riferimento che indica l'inizio di un dispositivo è dato dalle righe che iniziano con la lettera «T»:

T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=12  MxCh= 2
...
T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=12  MxCh= 0
...
T:  Bus=01 Lev=01 Prnt=01 Port=01 Cnt=02 Dev#=  3 Spd=12  MxCh= 0

Per conoscere il significato delle informazioni che appaiono in questo file, conviene consultare The Linux USB sub-system, di Brad Hards, citato anche alla fine del capitolo.

85.5.2   Dischi esterni

Vale la pena di annotare il caso particolare delle unità a disco collegate attraverso un bus USB. Quando vengono collegati, anche a caldo, dovrebbero essere individuati dal kernel, che li associa a unità SCSI. Anche in questo contesto può tornare utile cdrecord con l'opzione -scanbus:

cdrecord -scanbus[Invio]

Cdrecord 1.8 (i686-pc-linux-gnu) Copyright (C) 1995-2000 Jörg Schilling
Using libscg version 'schily-0.1'
scsibus0:
        0,0,0     0) *
        0,1,0     1) *
        0,2,0     2) 'FUJITSU ' 'M2513A          ' '1200' Removable Optical Storage
        0,3,0     3) 'TEAC    ' 'CD-R55S         ' '1.0E' Removable CD-ROM
        0,4,0     4) *
        0,5,0     5) *
        0,6,0     6) *
        0,7,0     7) *
scsibus1:
        1,0,0   100) 'LITE-ON ' 'LTR-12102B      ' 'NS1H' Removable CD-ROM
        1,1,0   101) *
        1,2,0   102) *
        1,3,0   103) *
        1,4,0   104) *
        1,5,0   105) *
        1,6,0   106) *
        1,7,0   107) *
scsibus2:
        2,0,0   200) 'IBM-DJSA' '-210            ' 'AB8A' Disk
        2,1,0   201) *
        2,2,0   202) *
        2,3,0   203) *
        2,4,0   204) *
        2,5,0   205) *
        2,6,0   206) *
        2,7,0   207) *

Trattandosi di un disco, dato che esiste già un'unità a disco nelle coordinate «0,2,0» (mentre gli altri componenti sono unità CD-ROM), è possibile accedere a questo con i file di dispositivo /dev/sdb*.

85.5.3   Avvio di un sistema GNU/Linux da un disco USB

L'avvio di un sistema GNU/Linux che risiede fisicamente in un disco USB esterno, richiede la preparazione di alcuni accorgimenti. Per cominciare, il kernel deve essere stato predisposto per la gestione del bus SCSI, del bus USB e in particolare per la gestione della memoria di massa su un bus USB (sezione 72.2.19). Ovviamente, se le funzionalità che consentono di accedere al disco USB sono spostate in moduli esterni al kernel, occorre predisporre un disco RAM iniziale per il caricamento di questi, ma qui si preferisce fare riferimento a un kernel che non abbia bisogno di questo accorgimento.

Durante l'avvio del kernel, è molto probabile che il rilevamento dell'unità a disco USB sia in ritardo rispetto al momento in cui viene fatto il tentativo di innestare il file system principale, cosa che si traduce normalmente con il blocco dovuto a un kernel panic. Per risolvere il problema in modo «standard», sembra non esserci altra strada che predisporre un disco RAM iniziale (capitolo 77), ma per chi è disposto a ricompilarsi il kernel dovrebbe essere più semplice introdurre nel codice del kernel stesso qualche istruzione per ritardare il procedimento, così da far trovare il disco USB quando è il momento.

Una prima soluzione, applicabile ai sorgenti di una versione 2.4, richiede di intervenire nel file sorgenti_linux/init/do_mounts.c, il cui contenuto cambia leggermente a seconda della versione. Vengono proposte qui delle modifiche, cercando di fare comprendere la collocazione corretta di queste, senza un legame troppo stretto con la versione del kernel. Queste modifiche derivano originariamente da un lavoro di Eric Lammerts, <http://www.lammerts.org/software/kernelpatches/>.

Il file sorgenti_linux/init/do_mounts.c contiene in particolare la funzione mount_block_root(), che dovrebbe avere l'aspetto seguente:

static void __init mount_block_root(char *name, int flags)
{
    char *fs_names = __getname();
    char *p;
    ...
retry:
    for (p = fs_names; *p; p += strlen(p)+1) {
        ...
        printk ("VFS: Cannot open root device \"%s\" or %s\n", ...);
        printk ("Please append a correct \"root=\" boot option\n");
        panic("VFS: Unable to mount root fs on %s", ...);

    }
    panic("VFS: Unable to mount root fs on %s", ...);
out:
    ...
}

Nella funzione mostrata appaiono diversi punti omessi, in particolare all'interno delle funzioni printk() che visualizzano dei messaggi. Infatti, gli argomenti successivi di queste funzioni cambiano in modo sostanziale a seconda della versione del kernel. Si osservi la presenza dell'etichetta retry, che ha lo scopo di consentire la ripetizione del tentativo di innesto del file system, attraverso un salto incondizionato. Ecco le modifiche proposte, con le quali si fanno sette tentativi, distanziati da una pausa di un secondo:

Listato 85.13. File sorgenti_linux/init/do_mounts.c; modifiche utili in un kernel 2.4.

static void __init mount_block_root(char *name, int flags)
{
    char *fs_names = __getname();
    char *p;
    ...
    int retries = 0;
    ...
retry:
    for (p = fs_names; *p; p += strlen(p)+1) {
        ...
        printk ("VFS: Cannot open root device \"%s\" or %s\n", ...);
        printk ("Please append a correct \"root=\" boot option\n");
        if (++retries > 7)
            panic("VFS: Unable to mount root fs on %s", ...);
        else {
            printk ("Retrying in 1 second, try #%d\n", retries);
            current->state = TASK_INTERRUPTIBLE;
            schedule_timeout(HZ);
            goto retry;
        }
    }
    panic("VFS: Unable to mount root fs on %s", ...);
out:
    ...
}

Come si può intuire, la funzione schedule_timeout(HZ) ha lo scopo di introdurre una pausa di un secondo, prima di ripetere il ciclo a partire dall'etichetta retry.

In alternativa, con i kernel 2.6 e anche 2.4 si può intervenire nel file sorgenti_linux/init/main.c (senza più toccare sorgenti_linux/init/do_mounts.c), per introdurre una pausa che viene fissata attraverso un parametro di avvio, creato appositamente: setupdelay. Questo tipo di modifica deriva da un lavoro di Willi Tarreay, pubblicato attraverso un messaggio del 2 maggio 2004 nella lista Linux-Kernel, che può essere letto presso <http://www.uwsg.iu.edu/hypermail/linux/kernel/0405.0/0252.html>.

La modifica che viene proposta richiede di aggiungere delle istruzioni al contenuto di sorgenti_linux/init/main.c. Lo schema seguente dovrebbe permettere di capire dove si deve intervenire; le righe aggiunte sono evidenziate con un carattere nero.

Listato 85.14. File sorgenti_linux/init/main.c; modifiche utili in un kernel 2.6.

...
...
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
static const char *panic_later, *panic_param;
static int setupdelay; /* delay some seconds to let
                          devices set up before root mount */

__setup("profile=", profile_setup);
 
static int __init setupdelay_setup(char *str)
{
    int par;
    if (get_option(&str,&par)) {
        setupdelay = par;
    }
    return 1;
}

__setup("setupdelay=", setupdelay_setup);

...
...
...
static int wait_setup_delay (void)
{
    int time_left;
    if (setupdelay) {
        printk("Waiting %d s to let devices set up properly ", setupdelay);
        time_left = setupdelay;
        while (time_left) {
            set_current_state (TASK_INTERRUPTIBLE);
            schedule_timeout (HZ);
            time_left--;
            printk(". ");
        }
        printk("\n");
    }
    return 1;
}

static int init(void * unused)
{
        ...
        ...
        /*
         * Do this before initcalls, because some drivers want to access
         * firmware files.
         */
        populate_rootfs();
 
        do_basic_setup();
 
        wait_setup_delay ();

        /*
         * check if there is an early userspace init.  If yes, let it do all
         * the work
         */
        if (sys_access((const char __user *) "/init", 0) == 0)
                execute_command = "/init";
        else
                prepare_namespace();

    ...
    ...

Listato 85.15. File sorgenti_linux/init/main.c; modifiche utili in un kernel 2.4.

...
...
static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
static int setupdelay; /* delay some seconds to let
                          devices set up before root mount */
static int __init setupdelay_setup(char *str)
{
    int par;
    if (get_option(&str,&par)) {
        setupdelay = par;
    }
    return 1;
}

__setup("setupdelay=", setupdelay_setup);
...
...
...
static int wait_setup_delay (void)
{
    int time_left;
    if (setupdelay) {
        printk("Waiting %d s to let devices set up properly ", setupdelay);
        time_left = setupdelay;
        while (time_left) {
            set_current_state (TASK_INTERRUPTIBLE);
            schedule_timeout (HZ);
            time_left--;
            printk(". ");
        }
        printk("\n");
    }
    return 1;
}
static int init(void * unused)
{
        struct files_struct *files;
        lock_kernel();
        do_basic_setup();
        wait_setup_delay ();
        prepare_namespace();
    ...
    ...

Naturalmente, per fare in modo che ci sia effettivamente un ritardo di n secondi, occorre usare l'opzione di avvio setupdelay=n.

Quanto mostrato dovrebbe risolvere il problema dell'avvio del sistema, ma rimane il fatto che alcuni dischi USB esterni rispondono in ritardo ai comandi che gli vengono impartiti. In pratica, ciò significa che quando si esegue il distacco di un file system contenuto in un disco USB, conviene aspettare un po' prima di spegnerlo o comunque di staccarlo fisicamente, per non rischiare di perdere i dati. Quando si arresta il sistema operativo, se il procedimento porta allo spegnimento automatico dell'elaboratore, diventa impossibile controllare questo problema se il file system contenuto nel disco USB è quello principale, pertanto conviene disabilitare la funzionalità attraverso un parametro di avvio del kernel:

apm=off

In alternativa, in modo più specifico:

apm=no-power-off

85.6   Riferimenti

Appunti di informatica libera 2007.02 --- Copyright © 2000-2007 Daniele Giacomini -- <daniele (ad) swlibero·org>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome usb.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico]

Valid ISO-HTML!

CSS validator!