Uwaga
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Ten temat zawiera krótkie omówienie transferów zbiorczych USB. Zawiera również instrukcje krok po kroku dotyczące sposobu, w jaki sterownik klienta może wysyłać i odbierać dane zbiorcze z urządzenia.
Informacje o punktach końcowych zbiorczych
Zbiorczy punkt końcowy USB może przesyłać duże ilości danych. Transfery zbiorcze są niezawodne, ponieważ umożliwiają wykrywanie błędów sprzętowych i obejmują ograniczoną liczbę ponownych prób w sprzęcie. W przypadku transferów do punktów końcowych zbiorczych przepustowość nie jest zarezerwowana w magistrali. Jeśli istnieje wiele żądań transferu przeznaczonych dla różnych typów punktów końcowych, kontroler najpierw planuje transfery danych krytycznych czasu, takich jak izochroniczne i przerywane pakiety. Tylko wtedy, gdy w magistrali jest dostępna nieużywana przepustowość, kontroler planuje transfery zbiorcze. Jeśli w autobusie nie ma innego znaczącego ruchu, transfer zbiorczy może być szybki. Jednak gdy magistrala jest zajęta innymi transferami, dane zbiorcze mogą czekać na czas nieokreślony.
Poniżej przedstawiono najważniejsze funkcje zbiorczego punktu końcowego:
- Zbiorcze punkty dostępowe są opcjonalne. Są one obsługiwane przez urządzenie USB, które chce przesyłać duże ilości danych. Na przykład przesyłanie plików na dysk flash, dane do lub z drukarki lub skanera.
- Urządzenia z pełną szybkością USB, dużą szybkością i superspeed obsługują zbiorcze punkty końcowe. Urządzenia o niskiej szybkości nie obsługują punktów końcowych zbiorczych.
- Punkt końcowy jest jednokierunkowy, a dane mogą być przesyłane w kierunku IN lub OUT. Zbiorczy punkt końcowy IN służy do odczytywania danych z urządzenia do hosta, a zbiorczy punkt końcowy OUT służy do wysyłania danych z hosta do urządzenia.
- Punkt końcowy ma bity CRC do sprawdzania błędów, a tym samym zapewnia integralność danych. W przypadku błędów CRC dane są automatycznie ponownie przesyłane.
- Punkt końcowy zbiorczy SuperSpeed może obsługiwać strumienie. Strumienie umożliwiają hostowi wysyłanie transferów do poszczególnych potoków strumienia.
- Maksymalny rozmiar pakietu zbiorczego punktu końcowego zależy od szybkości magistrali urządzenia. Maksymalne rozmiary pakietów dla pełnej prędkości, dużej prędkości i SuperSpeed to odpowiednio 64, 512 i 1024 bajty.
Transakcje zbiorcze
Podobnie jak wszystkie inne transfery USB, host zawsze inicjuje transfer zbiorczy. Komunikacja odbywa się między hostem a docelowym punktem końcowym. Protokół USB nie wymusza żadnego formatu na danych wysyłanych zbiorczo.
Sposób komunikacji hosta i urządzenia z magistralą zależy od szybkości, z jaką urządzenie jest połączone. W tej sekcji opisano kilka przykładów dużych szybkości i transferów zbiorczych SuperSpeed, które pokazują komunikację między hostem a urządzeniem.
Można zobaczyć strukturę transakcji i pakietów przy użyciu dowolnego analizatora USB, takiego jak Beagle, Ellisys, Analizatory protokołów USB LeCroy. Urządzenie analizatora pokazuje, jak dane są wysyłane do lub odbierane z urządzenia USB za pośrednictwem przewodu. W tym przykładzie przyjrzyjmy się niektórym śladom przechwyconym przez analizator USB LeCroy. Ten przykład dotyczy tylko informacji. Nie jest to poparcie firmy Microsoft.
Przykład transakcji zbiorczej OUT
Ten ślad analizatora przedstawia przykładową zbiorczą transakcję OUT z dużą szybkością.
W poprzednim przebiegu host inicjuje transfer masowy OUT do szybkiego punktu końcowego masowego, przesyłając pakiet tokenu z PID ustawionym na OUT (token OUT). Pakiet zawiera adres urządzenia i docelowego punktu końcowego. Po pakiecie OUT host wysyła pakiet danych zawierający ładunek zbiorczy. Jeśli punkt końcowy akceptuje dane przychodzące, wysyła pakiet ACK. W tym przykładzie widać, że host wysłał 31 bajtów na adres urządzenia:1; adres punktu końcowego: 2.
Jeśli punkt końcowy jest zajęty w momencie odebrania pakietu danych i nie może odbierać danych, urządzenie może wysłać pakiet NAK. W takim przypadku host rozpoczyna wysyłanie pakietów PING do urządzenia. Urządzenie odpowiada za pomocą pakietów NAK, o ile urządzenie nie jest gotowe do odbierania danych. Gdy urządzenie jest gotowe, odpowiada za pomocą pakietu ACK. Następnie host może wznowić transfer wychodzący.
Ten ślad analizatora przedstawia przykładową transakcję masowego transferu danych OUT o wysokiej prędkości.
W poprzednim śladzie host inicjuje transakcję OUT do punktu końcowego zbiorczego SuperSpeed, wysyłając pakiet danych. Pakiet danych zawiera adresy ładunku zbiorczego, urządzenia i punktu końcowego. W tym przykładzie widać, że host wysłał 31 bajtów na adres urządzenia:4; adres punktu końcowego: 2.
Urządzenie odbiera i potwierdza pakiet danych i wysyła pakiet ACK z powrotem do hosta. Jeśli punkt końcowy jest zajęty w momencie odebrania pakietu danych i nie może odbierać danych, urządzenie może wysłać pakiet NRDY. W przeciwieństwie do wysokiej prędkości, po otrzymaniu pakietu NRDY, host nie odpytywa urządzenia wielokrotnie. Zamiast tego host czeka na ERDY z urządzenia. Gdy urządzenie jest gotowe, wysyła pakiet ERDY, a następnie host może wysyłać dane do punktu końcowego.
Przykład transakcji zbiorczej typu IN
Ten ślad analizatora przedstawia przykładową zbiorczą transakcję IN z dużą szybkością.
W poprzednim śladzie host inicjuje transakcję, wysyłając pakiet tokenu z identyfikatorem PID ustawionym na IN (token IN). Następnie urządzenie wysyła pakiet danych z dużym ładunkiem. Jeśli punkt końcowy nie ma danych do wysłania lub nie jest jeszcze gotowy do wysłania danych, urządzenie może wysłać pakiet uzgadniania NAK. Host ponawia próbę transferu IN, dopóki nie otrzyma pakietu ACK z urządzenia. Pakiet ACK oznacza, że urządzenie zaakceptowało dane.
Ten ślad analizatora pokazuje przykładową transakcję zbiorczą SuperSpeed typu IN.
Aby zainicjować zbiorczy transfer IN z punktu końcowego SuperSpeed, host uruchamia transakcję zbiorczą, wysyłając pakiet ACK. Specyfikacja USB w wersji 3.0 optymalizuje tę początkową część transferu przez scalenie pakietów ACK i IN z jednym pakietem ACK. Zamiast tokenu IN w przypadku superspeed host wysyła token ACK w celu zainicjowania transferu zbiorczego. Urządzenie odpowiada pakietem danych. Następnie host potwierdza pakiet danych, wysyłając pakiet ACK. Jeśli punkt końcowy jest zajęty i nie można wysłać danych, urządzenie może wysłać stan NRDY. W takim przypadku host czeka, aż otrzyma pakiet ERDY z urządzenia.
Zadania sterownika klienta USB dla transferu zbiorczego
Aplikacja lub sterownik na hoście zawsze inicjuje transfer zbiorczy w celu wysyłania lub odbierania danych. Sterownik klienta przesyła żądanie do stosu sterowników USB. Stos sterowników USB programuje żądanie do kontrolera hosta, a następnie wysyła pakiety protokołu (zgodnie z opisem w poprzedniej sekcji) za pośrednictwem przewodu do urządzenia.
Zobaczmy, jak sterownik klienta przesyła żądanie przeniesienia zbiorczego w wyniku żądania aplikacji lub innego sterownika. Alternatywnie sterownik może zainicjować transfer samodzielnie. Niezależnie od metody, sterownik musi mieć bufor transferu oraz żądanie, aby zainicjować transfer zbiorczy.
W przypadku sterownika KMDF żądanie jest opisane w ramowym obiekcie żądania (zobacz Odwołanie do obiektu żądania WDF). Sterownik klienta wywołuje metody obiektu żądania, określając dojście WDFREQUEST w celu wysłania żądania do stosu sterowników USB. Jeśli sterownik klienta wysyła transfer zbiorczy w odpowiedzi na żądanie z aplikacji lub innego sterownika, framework tworzy obiekt żądania i dostarcza żądanie do sterownika klienta przy użyciu obiektu kolejki frameworka. W takim przypadku sterownik klienta może używać tego żądania do przesyłania transferu zbiorczego. Jeśli sterownik klienta zainicjował żądanie, sterownik może wybrać przydzielenie własnego obiektu żądania.
Jeśli aplikacja lub inny sterownik wysłał lub zażądał danych, bufor transferu jest przekazywany do sterownika przez strukturę. Alternatywnie sterownik klienta może przydzielić bufor transferu i utworzyć obiekt żądania, jeśli sterownik inicjuje transfer samodzielnie.
Poniżej przedstawiono główne zadania dotyczące sterownika klienta:
- Pobierz bufor transferu.
- Pobierz, sformatuj i wyślij obiekt żądania struktury do stosu sterowników USB.
- Zaimplementuj procedurę uzupełniania, aby otrzymywać powiadomienia, gdy stos sterowników USB zakończy żądanie.
W tym temacie opisano te zadania przy użyciu przykładu, w którym sterownik inicjuje transfer zbiorczy w wyniku żądania aplikacji do wysyłania lub odbierania danych.
Aby odczytać dane z urządzenia, sterownik klienta może użyć obiektu ciągłego czytnika udostępnianego przez framework. Aby uzyskać więcej informacji, zobacz How to use the continuous reader for reading data from a USB pipe (Jak używać ciągłego czytnika do odczytywania danych z potoku USB).
Przykład żądania transferu zbiorczego
Rozważmy przykładowy scenariusz, w którym aplikacja chce odczytywać lub zapisywać dane na urządzeniu. Aplikacja wywołuje interfejsy API systemu Windows w celu wysyłania takich żądań. W tym przykładzie aplikacja otwiera dojście do urządzenia przy użyciu identyfikatora GUID interfejsu urządzenia opublikowanego przez sterownik w trybie jądra. Następnie aplikacja wywołuje metodę ReadFile lub WriteFile , aby zainicjować żądanie odczytu lub zapisu. W tym wywołaniu aplikacja określa również bufor zawierający dane do odczytu lub zapisu oraz długość tego buforu.
Menedżer we/wy odbiera żądanie, tworzy pakiet żądań we/wy (IRP) i przekazuje go do sterownika klienta.
Struktura przechwytuje żądanie, tworzy obiekt żądania platformy i dodaje go do obiektu kolejki platformy. Platforma powiadamia następnie sterownik klienta o oczekiwaniu na przetworzenie nowego żądania. Powiadomienie odbywa się poprzez wywołanie procedur zwrotnych kolejki sterownika dla EvtIoRead lub EvtIoWrite.
Gdy platforma dostarcza żądanie do sterownika klienta, otrzymuje następujące parametry:
- WDFQUEUE dojście do obiektu kolejki struktury, który zawiera żądanie.
- Dojście WDFREQUEST do obiektu żądania platformy zawierającego szczegółowe informacje o tym żądaniu.
- Długość transferu, czyli liczba bajtów do odczytu lub zapisu.
W implementacji sterownika klienta EvtIoRead lub EvtIoWrite sterownik sprawdza parametry żądania i opcjonalnie może przeprowadzić sprawdzanie poprawności.
Jeśli używasz strumieni punktu końcowego zbiorczego SuperSpeed, wyślesz żądanie w URB, ponieważ KMDF nie obsługuje strumieni w sposób wbudowany. Aby uzyskać informacje na temat przesyłania żądania transferu do strumieni zbiorczego punktu końcowego, zobacz Jak otwierać i zamykać strumienie statyczne w zbiorczym punkcie końcowym USB.
Jeśli nie używasz strumieni, możesz użyć metod zdefiniowanych przez usługę KMDF, aby wysłać żądanie zgodnie z opisem w poniższej procedurze:
Wymagania wstępne
Przed rozpoczęciem upewnij się, że masz następujące informacje:
Sterownik klienta musiał utworzyć obiekt urządzenia docelowego USB platformy i uzyskać dojście WDFUSBDEVICE przez wywołanie metody WdfUsbTargetDeviceCreateWithParameters.
Jeśli używasz szablonów USB dostarczanych z programem Microsoft Visual Studio Professional 2012, kod szablonu wykonuje te zadania. Kod szablonu uzyskuje dojście do obiektu urządzenia docelowego i przechowuje je w kontekście urządzenia. Aby uzyskać więcej informacji, zobacz "Kod źródłowy urządzenia" w Opis struktury kodu sterownika klienta USB (KMDF).
Dojście WDFREQUEST do obiektu żądania platformy zawierającego szczegółowe informacje o tym żądaniu.
Liczba bajtów do odczytu lub zapisu.
Interfejs WDFUSBPIPE obsługuje obiekt potoku struktury skojarzony z docelowym punktem końcowym. Należy uzyskać uchwyty potoku podczas konfiguracji urządzenia, wyliczając potoki. Aby uzyskać więcej informacji, zobacz Jak enumerować potoki USB.
Jeśli zbiorczy punkt końcowy obsługuje strumienie, musisz mieć dojście potoku do strumienia. Aby uzyskać więcej informacji, zobacz Jak otwierać i zamykać strumienie statyczne w punkcie końcowym zbiorczym USB.
Krok 1: Pobierz bufor transferu
Bufor transferu lub bufor transferu MDL zawiera dane do wysyłania lub odbierania. W tym temacie założono, że wysyłasz lub odbierasz dane w buforze transferu. Bufor transferu jest opisany w obiekcie pamięci WDF (zobacz Odwołanie do obiektu pamięci WDF). Aby uzyskać obiekt pamięci skojarzony z buforem transferu, wywołaj jedną z następujących metod:
- W przypadku zbiorczego żądania transferu IN wywołaj metodę WdfRequestRetrieveOutputMemory .
- W przypadku zbiorczego żądania transferu OUT wywołaj metodę WdfRequestRetrieveInputMemory .
Sterownik klienta nie musi zwalniać tej pamięci. Pamięć jest skojarzona z obiektem żądania nadrzędnego i jest zwalniana, gdy element nadrzędny jest zwalniany.
Krok 2. Formatowanie i wysyłanie obiektu żądania platformy do stosu sterowników USB
Żądanie przeniesienia można wysłać asynchronicznie lub synchronicznie.
Są to metody asynchroniczne:
Metody na tej liście sformatuj żądanie. Jeśli żądanie jest wysyłane asynchronicznie, ustaw wskaźnik na procedurę uzupełniania zaimplementowaną przez sterownik, wywołując metodę WdfRequestSetCompletionRoutine (opisaną w następnym kroku). Aby wysłać żądanie, wywołaj metodę WdfRequestSend .
Jeśli żądanie jest wysyłane synchronicznie, wywołaj następujące metody:
- WdfUsbTargetPipeReadSynchronously
- WdfUsbTargetPipeWriteSynchronously (Synchroniczny zapis do rury docelowej USB za pomocą Wdf)
Przykłady kodu można znaleźć w sekcji Przykłady tematów referencyjnych dotyczących tych metod.
Krok 3. Implementowanie procedury uzupełniania dla żądania
Jeśli żądanie jest wysyłane asynchronicznie, należy zaimplementować procedurę uzupełniania, aby otrzymywać powiadomienia, gdy stos sterowników USB zakończy żądanie. Po zakończeniu platforma wywołuje procedurę zakończenia sterownika. Struktura przekazuje następujące parametry:
- Dojście WDFREQUEST do obiektu żądania.
- WDFIOTARGET uchwyt do obiektu docelowego I/O dla żądania.
- Wskaźnik do struktury WDF_REQUEST_COMPLETION_PARAMS zawierającej informacje o zakończeniu. Informacje specyficzne dla usb znajdują się w elemencie CompletionParams-Parameters.Usb>.
- Funkcja WDFCONTEXT obsługuje kontekst określony przez sterownik w wywołaniu metody WdfRequestSetCompletionRoutine.
W procedurze uzupełniania wykonaj następujące zadania:
Sprawdź stan żądania, uzyskując wartość CompletionParams-IoStatus.Status>.
Sprawdź stan USBD ustawiony przez stos sterownika USB.
W przypadku błędów potoku wykonaj działania naprawcze. Aby uzyskać więcej informacji, zobacz Jak odzyskać z błędów potoku USB.
Sprawdź liczbę przetransferowanych bajtów.
Transfer zbiorczy jest ukończony, gdy żądana liczba bajtów została przeniesiona do lub z urządzenia. Jeśli wysyłasz bufor żądania przez wywołanie metody KMDF, sprawdź wartość odebraną w elemencie CompletionParams-Parameters.Usb.Completion-Parameters.PipeWrite.Length>> lub CompletionParams-Parameters.Usb.Completion-Parameters.PipeRead.Length>>.
W prostym transferze, w którym stos sterowników USB wysyła wszystkie żądane bajty w jednym pakiecie danych, możesz sprawdzić porównanie wartości Length z żądaną liczbą bajtów. Jeśli stos sterowników USB przesyła żądanie w wielu pakietach danych, należy śledzić liczbę przetransferowanych bajtów i pozostałą liczbę bajtów.
Jeśli całkowita liczba bajtów została przeniesiona, ukończ żądanie. Jeśli wystąpił błąd, ukończ żądanie ze zwróconym kodem błędu. Wykonaj żądanie, wywołując metodę WdfRequestComplete . Jeśli chcesz ustawić informacje, takie jak liczba przetransferowanych bajtów, wywołaj metodę WdfRequestCompleteWithInformation.
Upewnij się, że po zakończeniu żądania z informacjami liczba bajtów musi być równa lub mniejsza niż liczba żądanych bajtów. Struktura weryfikuje te wartości. Jeśli długość ustawiona w ukończonym żądaniu jest większa niż długość oryginalnego żądania, może wystąpić sprawdzanie błędów.
W tym przykładowym kodzie pokazano, jak sterownik klienta może przesłać żądanie transferu zbiorczego. Sterownik ustawia procedurę zakończenia. Ta rutyna jest wyświetlana w następnym bloku kodu.
/*++
Routine Description:
This routine sends a bulk write request to the
USB driver stack. The request is sent asynchronously and
the driver gets notified through a completion routine.
Arguments:
Queue - Handle to a framework queue object.
Request - Handle to the framework request object.
Length - Number of bytes to transfer.
Return Value:
VOID
--*/
VOID Fx3EvtIoWrite(
IN WDFQUEUE Queue,
IN WDFREQUEST Request,
IN size_t Length
)
{
NTSTATUS status;
WDFUSBPIPE pipe;
WDFMEMORY reqMemory;
PDEVICE_CONTEXT pDeviceContext;
pDeviceContext = GetDeviceContext(WdfIoQueueGetDevice(Queue));
pipe = pDeviceContext->BulkWritePipe;
status = WdfRequestRetrieveInputMemory(
Request,
&reqMemory
);
if (!NT_SUCCESS(status))
{
goto Exit;
}
status = WdfUsbTargetPipeFormatRequestForWrite(
pipe,
Request,
reqMemory,
NULL
);
if (!NT_SUCCESS(status))
{
goto Exit;
}
WdfRequestSetCompletionRoutine(
Request,
BulkWriteComplete,
pipe
);
if (WdfRequestSend( Request,
WdfUsbTargetPipeGetIoTarget(pipe),
WDF_NO_SEND_OPTIONS) == FALSE)
{
status = WdfRequestGetStatus(Request);
goto Exit;
}
Exit:
if (!NT_SUCCESS(status)) {
WdfRequestCompleteWithInformation(
Request,
status,
0
);
}
return;
}
Kod przykładowy przedstawia implementację rutyny kończenia dla transmisji zbiorczej. Sterownik klienta kończy żądanie w procedurze uzupełniania i ustawia te informacje o żądaniu: stan i liczbę przetransferowanych bajtów.
/*++
Routine Description:
This completion routine is invoked by the framework when
the USB drive stack completes the previously sent
bulk write request. The client driver completes the
the request if the total number of bytes were transferred
to the device.
In case of failure it queues a work item to start the
error recovery by resetting the target pipe.
Arguments:
Queue - Handle to a framework queue object.
Request - Handle to the framework request object.
Length - Number of bytes to transfer.
Pipe - Handle to the pipe that is the target for this request.
Return Value:
VOID
--*/
VOID BulkWriteComplete(
_In_ WDFREQUEST Request,
_In_ WDFIOTARGET Target,
PWDF_REQUEST_COMPLETION_PARAMS CompletionParams,
_In_ WDFCONTEXT Context
)
{
PDEVICE_CONTEXT deviceContext;
size_t bytesTransferred=0;
NTSTATUS status;
UNREFERENCED_PARAMETER (Target);
UNREFERENCED_PARAMETER (Context);
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"In completion routine for Bulk transfer.\n"));
// Get the device context. This is the context structure that
// the client driver provided when it sent the request.
deviceContext = (PDEVICE_CONTEXT)Context;
// Get the status of the request
status = CompletionParams->IoStatus.Status;
if (!NT_SUCCESS (status))
{
// Get the USBD status code for more information about the error condition.
status = CompletionParams->Parameters.Usb.Completion->UsbdStatus;
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Bulk transfer failed. 0x%x\n",
status));
// Queue a work item to start the reset-operation on the pipe
// Not shown.
goto Exit;
}
// Get the actual number of bytes transferred.
bytesTransferred =
CompletionParams->Parameters.Usb.Completion->Parameters.PipeWrite.Length;
KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
"Bulk transfer completed. Transferred %d bytes. \n",
bytesTransferred));
Exit:
// Complete the request and update the request with
// information about the status code and number of bytes transferred.
WdfRequestCompleteWithInformation(Request, status, bytesTransferred);
return;
}
Tematy pokrewne
- Transfery USB wejścia/wyjścia
- Jak otwierać i zamykać strumienie statyczne w punkcie końcowym zbiorczym USB