Piszę sterownik jądra Linuksa o niestandardowe urządzenie USB, które będzie używać luźnych punktów końcowych, wszystko wydaje się działać dobrze, jednak otrzymuję bardzo wolne szybkości transmisji danych. W szczególności zajmuje około 25 sekund na zapisanie i odczytanie danych o wartości 10 MB. Próbowałem tego w systemie wbudowanym i maszynie wirtualnej Linux działającej na rozsądnym komputerze z podobnymi wynikami.bardzo słaba wydajność (~ 0,4MB/s) z linuksowym ładowaniem jądra USB i przeniesieniem jądra sprzętowego
Używam zestawu rozwojowego EZ-USB FX2 z Cypress jako planszy docelowej. To działa oprogramowanie układowe, które ustawia dwa i dwa końcowe punkty końcowe. Każdy punkt końcowy jest podwójnie buforowany i obsługuje 512-bajtowe okna. Oprogramowanie układa sondę końcową przez pętlę while (1) w trybie main(), bez uśpienia i kopiuje dane z punktu końcowego do punktu końcowego, gdy dane te są dostępne za pomocą operatorów automatycznych. Powiedziano mi, że może to przenosić dane w sposób uczciwy w systemie Windows przy użyciu ich konkretnych aplikacji, ale nie miałem okazji zweryfikować tego.
Mój kod (odpowiednie fragmenty poniżej) wywołuje funkcję o nazwie bulk_io w procedurze sondy urządzenia. Ta funkcja tworzy liczbę (URB_SETS) spośród urb, które próbują zapisać 512 bajtów na urządzeniu. Zmiana tej liczby z zakresu od 1 do 32 nie zmienia wydajności. Wszystkie kopiują z tego samego bufora. Procedura obsługi wywołania zwrotnego dla każdej operacji zapisu do punktu końcowego jest używana do utworzenia adresu URL odczytu na odpowiednim punkcie końcowym. Oddzwaniane wywołanie tworzy kolejne urb do zapisu, dopóki nie osiągnę całkowitej liczby żądań zapisu/odczytu, które chcę uruchomić w tym samym czasie (20 000). Pracuję teraz, aby większość operacji w funkcjach wywołania zwrotnego przenieść na dolne połówki w przypadku, gdy blokują one inne przerwania. Zastanawiam się również nad przepisaniem oprogramowania układowego pętli zbiorczej dla Cypress FX2, aby używał przerwania zamiast odpytywania. Czy jest tu coś, co wygląda na nietypowe, aby wydajność była tak niska? Z góry dziękuję. Daj mi znać, jeśli chcesz zobaczyć więcej kodu, to po prostu niezaprogramowany sterownik do testowania operacji wejścia/wyjścia na Cypress FX2.
To się końcowy funkcja zapisu zwrotna:
static void bulk_io_out_callback0(struct urb *t_urb) {
// will need to make this work with bottom half
struct usb_dev_stat *uds = t_urb->context;
struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io_out_callback0: out of memory!");
}
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_rcvbulkpipe(uds->udev,uds->ep_in[0]), uds->buf_in, uds->max_packet, bulk_io_in_callback0, uds);
usb_submit_urb(urb0,GFP_KERNEL);
usb_free_urb(urb0);
}
Jest to w końcowym odczytać funkcję callback:
static void bulk_io_in_callback0(struct urb *t_urb) {
struct usb_dev_stat *uds = t_urb->context;
struct urb *urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io_out_callback0: out of memory!");
}
if (uds->seq--) {
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds);
usb_submit_urb(urb0,GFP_KERNEL);
}
else {
uds->t1 = get_seconds();
uds->buf_in[9] = 0; // to ensure we only print the first 8 chars below
printk("bulk_io_in_callback0: completed, time=%lds, bytes=%d, data=%s\n", (uds->t1-uds->t0), uds->max_packet*SEQ, uds->buf_in);
}
usb_free_urb(urb0);
}
Ta funkcja jest wywoływana skonfigurować początkowe Urbs:
static int bulk_io (struct usb_interface *interface, struct usb_dev_stat *uds) {
struct urb *urb0;
int i;
uds->t0 = get_seconds();
memcpy(uds->buf_out,"abcd1234",8);
uds->seq = SEQ; // how many times we will run this
printk("bulk_io: starting up the stream, seq=%ld\n", uds->seq);
for (i = 0; i < URB_SETS; i++) {
urb0 = usb_alloc_urb(0,GFP_KERNEL);
if (urb0 == NULL) {
printk("bulk_io: out of memory!\n");
return(-1);
}
usb_fill_bulk_urb(urb0, interface_to_usbdev(uds->intf), usb_sndbulkpipe(uds->udev,uds->ep_out[0]), uds->buf_out, uds->max_packet, bulk_io_out_callback0, uds);
printk("bulk_io: submitted urb, status=%d\n", usb_submit_urb(urb0,GFP_KERNEL));
usb_free_urb(urb0); // we don't need this anymore
}
return(0);
}
Edytuj 1 Weryfikuję ed że udev-> prędkość == 3, więc USB_SPEED_HIGH, czyli to nie jest, ponieważ Linux uważa, że to powolne urządzenie ....
Edycja 2 przeniosłem wszystko w wywołań zwrotnych związanych z tworzeniem URB (kmalloc, przesłać) i uwolnienie do dolnej połowy, tej samej wydajności.
Tajemnica już nie istnieje. Zmodyfikowałem oprogramowanie "bulkloop" CY7C68013A, aby przełączać GPIO, gdy przesuwa on dane/uzbrajające punkty końcowe i spędzało ~ 80% swoich cykli, wykonując tę funkcję. Wygląda na to, że rdzeń 8051 dotyka buforów USB ze wszystkimi ograniczeniami przepustowości do ~ 0.5MB/s, jak pokazano powyżej. Poszedłem do przodu i testowałem ich demo bulkloop z biblioteki CyUSB lib i osiągałem znacznie gorszą wydajność, około 0.1MB/s. Podsumowując, korzystanie z oprogramowania układowego luzem nie jest dobrym testem wydajności sterownika USB. Spróbuję go z FPGA dostarczającym dane CY7C68013A. – armguy
tylko mały punkt tutaj, powinieneś utrzymywać callbacki URB tak małe, jak to możliwe (np. Po prostu ustawienie flagi) –