Come eseguire il ripristino da errori di pipe USB
Nota
Questo articolo è destinato agli sviluppatori di driver di dispositivo. Se si riscontrano difficoltà con un dispositivo USB, vedere Risolvere i problemi USB-C in Windows
Questo articolo fornisce informazioni sui passaggi che è possibile provare quando un trasferimento di dati a una pipe USB ha esito negativo. I meccanismi descritti in questo articolo illustrano le operazioni di interruzione, reimpostazione e ciclo delle porte su pipe bulk, interrupt e isocrone.
Un driver client USB comunica con il dispositivo inviando trasferimenti di controllo all'endpoint predefinito; trasferimenti di dati a endpoint bulk, interrupt e isocroni del dispositivo. A volte, questi trasferimenti possono non riuscire a causa di vari motivi, ad esempio una condizione di stallo nell'endpoint. Se il trasferimento non riesce, la pipe associata non può elaborare le richieste fino a quando non viene cancellata la condizione di errore.
Per i trasferimenti di controllo, lo stack di driver USB cancella automaticamente le condizioni di errore. Per i trasferimenti di dati, il client deve eseguire i passaggi appropriati per il ripristino dalla condizione di errore. Quando un trasferimento dei dati non riesce, lo stack di driver USB segnala l'errore al driver client tramite codici di stato USBD non riusciti. In base al codice di stato, il driver può quindi fornire un meccanismo di ripristino degli errori.
Questo articolo fornisce linee guida sul ripristino degli errori tramite queste operazioni.
- Reimpostare la pipe USB
- Reimpostare la porta USB a cui è connesso il dispositivo
- Scorrere la porta USB per enumerare nuovamente lo stack di dispositivi per il driver client
Per cancellare una condizione di errore, iniziare con l'operazione reset-pipe ed eseguire operazioni più complesse, ad esempio reset-port e cycle-port, solo se necessario.
Informazioni sul coordinamento di vari meccanismi di ripristino:
Il driver client deve coordinare le diverse operazioni per il ripristino e assicurarsi che venga usato un solo metodo in un determinato momento. Si consideri, ad esempio, un dispositivo con due endpoint: un bulk e un interrupt. Dopo l'invio di alcune richieste di trasferimento dei dati al dispositivo, il driver rileva che le richieste hanno esito negativo sulla pipe bulk. Per eseguire il ripristino da questi errori, il driver reimposta la pipe bulk. Tuttavia, l'operazione non risolve gli errori di trasferimento e i trasferimenti in blocco continuano a non riuscire. Di conseguenza, il driver invia una richiesta di reimpostazione della porta USB. Nel frattempo, i trasferimenti iniziano a non riuscire nella pipe di interrupt e quindi una richiesta di reimpostazione del dispositivo. Per eseguire il ripristino dagli errori di trasferimento degli interrupt, il driver invia una richiesta di reimpostazione pipe sulla pipe di interrupt. Se queste due operazioni non sono coordinate, il driver può avviare due operazioni di ripristino del dispositivo contemporaneamente, a causa di errori su entrambe le pipe. Queste operazioni simultanee possono essere problematiche.
Il driver client deve assicurarsi che in un determinato momento, il driver esegue una sola operazione di reimpostazione della porta o della porta ciclica. Durante queste operazioni, un'operazione reset pipe non deve essere in corso su alcuna pipe e il driver non deve inviare una nuova richiesta di reimpostazione pipe.
Informazioni importanti
Questo articolo usa Kernel-Mode Driver Framework (KMDF).
Prerequisiti
Il driver client deve aver creato l'oggetto dispositivo di destinazione USB del framework.
Se si usano i modelli USB forniti con Microsoft Visual Studio Professional 2012, il codice del modello esegue tali attività. Il codice del modello ottiene l'handle per l'oggetto dispositivo di destinazione e archivia nel contesto del dispositivo.
Un driver client KMDF deve ottenere un handle WDFUSBDEVICE chiamando il metodo WdfUsbTargetDeviceCreateWithParameters. Per altre informazioni, vedere "Codice sorgente del dispositivo" in Informazioni sulla struttura del codice del driver client USB (KMDF).
Il driver client deve avere un handle per l'oggetto della pipe di destinazione del framework. Per altre informazioni, vedere Come enumerare le pipe USB.
Passaggio 1: Determinare la causa della condizione di errore
Il driver client avvia un trasferimento dei dati usando un blocco di richieste USB (ODBC). Al termine della richiesta, lo stack di driver USB restituisce un codice di stato USBD che indica se il trasferimento è riuscito o non è riuscito. In un errore, il codice USBD indica il motivo dell'errore.
- Se è stato inviato l'opzione PST chiamando il metodo WdfUsbTargetDeviceSendUrbSynchronously, controllare il membro Hdr.Status della struttura HDR dopo la restituzione del metodo.
- Se è stato inviato IL COMANDO IN modo asincrono chiamando il metodo WdfRequestSend, controllare lo stato DI ARCHIVIAZIONe nel EVT_WDF_REQUEST_COMPLETION_ROUTINE. Il parametro Params punta a una struttura WDF_REQUEST_COMPLETION_PARAMS . Per controllare il codice di stato USBD, controllare il membro Usb-UsbdStatus>. Per informazioni sul codice, vedere USBD_STATUS.
Gli errori di trasferimento possono derivare da un errore del dispositivo, ad esempio USBD_STATUS_STALL_PID o USBD_STATUS_BABBLE_DETECTED. Possono anche verificarsi a causa di un errore segnalato dal controller host, ad esempio USBD_STATUS_XACT_ERROR.
Passaggio 2: Determinare se il dispositivo è connesso alla porta
Prima di inviare qualsiasi richiesta che reimposta la pipe o il dispositivo, assicurarsi che il dispositivo sia connesso. È possibile determinare lo stato connesso del dispositivo chiamando il metodo WdfUsbTargetDeviceIsConnectedSynchronous.
Passaggio 3: Annullare tutti i trasferimenti in sospeso alla pipe
Prima di inviare richieste che reimpostano la pipe o la porta, annullare tutte le richieste di trasferimento in sospeso alla pipe, che lo stack di driver USB non è ancora stato completato. È possibile annullare le richieste in uno dei modi seguenti:
Arrestare la destinazione di I/O chiamando il metodo WdfIoTargetStop.
Per arrestare prima di tutto la destinazione di I/O, ottenere l'handle WDFIOTARGET associato all'oggetto pipe del framework chiamando il metodo WdfUsbTargetPipeGetIoTarget. Usando l'handle, chiamare WdfIoTargetStop. Nella chiamata impostare l'azione su WdfIoTargetCancelSentIo (vedere WDF_IO_TARGET_SENT_IO_ACTION)** per indicare al framework di annullare tutte le richieste che lo stack di driver USB non è stato completato. Per le richieste completate, il driver client deve attendere che il callback di completamento venga richiamato dal framework.
Inviare una richiesta di interruzione pipe. È possibile inviare la richiesta chiamando uno di questi metodi:
Chiamare il metodo WdfUsbTargetPipeAbortSynchronously.
La chiamata è sincrona e restituisce solo dopo l'annullamento di tutte le richieste in sospeso. WdfUsbTargetPipeAbortSynchronously accetta un parametro Request facoltativo. È consigliabile passare un handle WDFREQUEST a un oggetto richiesta framework preallocato. Il parametro abilita il framework di utilizzo dell'oggetto richiesta specificato anziché un oggetto richiesta interno a cui il driver non può accedere. Questo valore di parametro garantisce che WdfUsbTargetPipeAbortSynchronously non riesca a causa di memoria insufficiente.
Chiamare il metodo WdfUsbTargetPipeFormatRequestForAbort per formattare un oggetto richiesta per una richiesta abort-pipe e quindi inviare la richiesta chiamando il metodo WdfRequestSend.
Se il driver invia la richiesta in modo asincrono, deve specificare un puntatore al EVT_WDF_REQUEST_COMPLETION_ROUTINE del driver implementato dal driver. Per specificare il puntatore, chiamare il metodo WdfRequestSetCompletionRoutine.
Il driver può inviare la richiesta in modo sincrono specificando WDF_REQUEST_SEND_OPTION_SYNCHRONOUS come una delle opzioni di richiesta in WdfRequestSend. Se si invia la richiesta in modo sincrono, chiamare invece WdfUsbTargetPipeAbortSynchronously.
Passaggio 4: Reimpostare la pipe USB
Avviare il ripristino degli errori reimpostando la pipe. È possibile inviare una richiesta reset-pipe chiamando uno di questi metodi:
Chiamare WdfUsbTargetPipeResetSynchronously per inviare una richiesta di reimpostazione pipe in modo sincrono.
Chiamare il metodo WdfUsbTargetPipeFormatRequestForReset per formattare un oggetto richiesta per una richiesta reset-pipe e quindi inviare la richiesta chiamando il metodo WdfRequestSend. Tali chiamate sono simili a quelle per la richiesta abort-pipe, come descritto nel passaggio 3.
Nota
Non inviare nuove richieste di trasferimento fino al completamento dell'operazione reset-pipe.
La richiesta reset-pipe cancella la condizione di errore nel dispositivo e l'hardware del controller host. Per cancellare l'errore del dispositivo, lo stack di driver USB invia una richiesta di controllo CLEAR_FEATURE al dispositivo usando il selettore di funzionalità ENDPOINT_HALT. Il destinatario della richiesta è l'endpoint associato alla pipe. Se la condizione di errore si è verificata in una pipe isocrona, lo stack di driver non esegue alcuna azione per cancellare il dispositivo perché, in caso di errori, gli endpoint isocroni vengono cancellati automaticamente.
Per cancellare l'errore del controller host, lo stack del driver cancella lo stato HALT della pipe e reimposta l'interruttore dei dati della pipe su 0.
Passaggio 5: Reimpostare la porta USB
Se un'operazione reset-pipe non cancella la condizione di errore e i trasferimenti di dati continuano a non riuscire, inviare una richiesta di reimpostazione della porta.
Annullare tutti i trasferimenti al dispositivo. A tale scopo, enumerare tutte le pipe nella configurazione corrente e annullare le richieste in sospeso pianificate per ogni pipe.
Arrestare la destinazione di I/O per il dispositivo.
Chiamare il metodo WdfUsbTargetDeviceGetIoTarget per ottenere un handle WDFIOTARGET associato all'oggetto dispositivo di destinazione del framework. Chiamare quindi WdfIoTargetStop e specificare l'handle WDFIOTARGET. In chiamata impostare l'azione su WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Inviare una richiesta di reimpostazione della porta chiamando il metodo WdfUsbTargetDeviceResetPortSynchronously.
Un'operazione di reimpostazione della porta fa sì che il dispositivo venga nuovamente enumerato sul bus USB. Lo stack di driver USB mantiene la configurazione del dispositivo dopo l'enumerazione . Il driver client può usare gli handle di pipe ottenuti in precedenza perché lo stack di driver garantisce che gli handle di pipe esistenti rimangano validi.
Non è possibile reimpostare una singola funzione di un dispositivo composito. Per un dispositivo composito, quando il driver client di una determinata funzione invia una richiesta di reimpostazione della porta, viene reimpostato l'intero dispositivo. Se il dispositivo USB mantiene lo stato, la richiesta di reimpostazione della porta può influire sui driver client di altre funzioni. È quindi importante che il driver client tenti di reimpostare la pipe prima di reimpostare la porta.
Passaggio 6: Scorrere la porta USB
Un'operazione di porta del ciclo è simile al dispositivo scollegato e collegato alla porta, ad eccezione del fatto che il dispositivo non è disconnesso elettricamente. Il dispositivo viene disconnesso e riconnesso nel software. Questa operazione comporta la reimpostazione e l'enumerazione del dispositivo. Di conseguenza, PnP Manager ricompila il nodo del dispositivo.
Se un'operazione di reimpostazione della porta non cancella la condizione di errore e i trasferimenti di dati continuano a non riuscire, inviare una richiesta di porta del ciclo.
Annullare tutti i trasferimenti al dispositivo. Assicurarsi di annullare la richiesta in sospeso pianificata per ogni pipe nella configurazione corrente (vedere il passaggio 3).
Arrestare la destinazione di I/O per il dispositivo.
Chiamare il metodo WdfUsbTargetDeviceGetIoTarget per ottenere un handle WDFIOTARGET associato all'oggetto dispositivo di destinazione del framework. Chiamare quindi WdfIoTargetStop e specificare l'handle WDFIOTARGET. In chiamata impostare l'azione su WdfIoTargetCancelSentIo (WDF_IO_TARGET_SENT_IO_ACTION).
Inviare una richiesta di porta ciclica chiamando uno di questi metodi:
- Chiamare WdfUsbTargetDeviceCyclePortSynchronously per inviare in modo sincrono una richiesta di porta ciclo.
- Chiamare il metodo WdfUsbTargetDeviceFormatRequestForCyclePort per formattare un oggetto richiesta per una richiesta di porta ciclo e quindi inviare la richiesta chiamando il metodo WdfRequestSend. Tali chiamate sono simili a quelle per la richiesta abort-pipe, come descritto nel passaggio 3.
Il driver client può inviare richieste di trasferimento al dispositivo solo dopo il completamento della richiesta di porta del ciclo. Questo perché il nodo del dispositivo viene rimosso mentre lo stack di driver USB elabora la richiesta di porta del ciclo.
La richiesta di porta del ciclo fa sì che il dispositivo venga rienumerazione. Lo stack di driver USB informa PnP Manager che il dispositivo è stato disconnesso. PnP Manager rimuove lo stack di dispositivi associato al driver client. Lo stack di driver reimposta il dispositivo, lo enumera nuovamente sul bus USB e informa PnP Manager che un dispositivo è stato connesso. PnP Manager ricompila quindi lo stack di dispositivi per il dispositivo USB.
Come risultato dell'operazione di porta del ciclo, qualsiasi applicazione con un handle aperto al dispositivo riceve una notifica di rimozione del dispositivo (se l'applicazione registrata per tale notifica). In risposta, l'applicazione potrebbe segnalare un messaggio disconnesso dal dispositivo all'utente. Poiché influisce sull'esperienza utente, il driver client deve optare per una richiesta di porta ciclica solo se altri meccanismi di ripristino non risolvono la condizione di errore.
Analogamente all'operazione di reimpostazione della porta (descritta nel passaggio 6), per un dispositivo composito, l'operazione di porta del ciclo influisce sull'intero dispositivo e non sulle singole funzioni del dispositivo.