Elektroda.pl
Elektroda.pl
X
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Pisanie sterownika USB pod system linux.

22 Sie 2009 16:02 4629 9
  • Poziom 9  
    Witam, wszystkich forumowiczów. Jest to mój pierwszy post więc chciałem się przywitać!

    Mam nadzieję, że ktoś będzie mi w stanie pomóc z następującym problemem:

    jestem w trakcie pisania sterownika dla przetwornika A/D dla linuxa, kóry jest moim pierwszym a zarazem jest projektem zaliczeniowym na moich studiach. Podłączony jest on poprzez USB. Płytka ma 2 endpoiny z których jeden jest IN a drugi OUT, oba w trbie bulk, - sterowanie pracą i odbieranie danych.
    Przy wykorzystaniu przykładu z jądra usb-skeleton.c udało mi się nawiązać łączność z płytką. Wysłałem jej polecenie na które przedstawiła mi się.

    Teraz muszę spełnić wymagania. Sterownik ma umożliwić wielu programom dostęp do danych przy czym każdy program może zarządać dostępu do innego kanału i różnej częstotliwości. Ja muszę je jakoś pogodzić i wybrać najbardziej wypośrodkowaną możliwość.

    Gdzie jest problem?
    Nie rozumiem jak zachowuje się system przy połączeniu bulk. Gdy włączę próbkowanie i płytka zacznie wysyłać mi dane to kto je będzie odbierał? Jeżeli nikt to dojedzie do przepełnienia? Czy jest tak, że wywołanie read idzie od programu z przestrzeni użytkownika czy sterownik sam może siebie jakoś wywołać i zciągnąć od płytki dane i dodać do jakiegoś buforu?
    Nie udało mi się nigdzie znaleźć sensownego rozwiązania tego problemu, ani tego jak to w praktyce wygląda.

    Mam nadzieję, że znajdzie się ktoś na tyle dobry żeby naprowadzić mnie chociaż na trop...
  • Poziom 15  
    Wtiam!
    Z tego co się orientuję, to transferem zarządza HOST, więc sama płytka nie może wysyłać danych w trybie BULK bez uprzedniego pozwolenia.

    polecam:
    http://www.beyondlogic.org/usbnutshell/usb4.htm#Bulk

    Widzę również, że w usb-skeleton.c jest funkcja skel_read, w której podajesz bufor na odebrane dane i liczbę danych do odebrania.

    Pozdrawiam
    Wojt
  • Poziom 9  
    Tak, ale...
    Jeżeli odbiorę dane poprzez jeden proces to będę musiał się nimi jakoś podzielić z drugim.

    Mam swój pomysł... Istnieje f-cja wykonywana po tym jak tylko oddelegowany urb zakończy swoje działanie. Po jednym odczycie w tej funkcji mógłbym zlecać następne odczyty. Wszystko inicjowało by się w ioctl a potem działało jak łańcuch. Dane szły by do buforów.

    Czy może ktoś mi powiedzieć, że to dobry trop? :)
  • Poziom 21  
    Witaj kolego.

    Ja używam Linuxa Ubuntu i poczytałem conieco na temat USB i np mikrokontrolera STM32 jak co się odbywa. W USB jest tak jak podłączasz slave-a np. pendrive, USB->RS232 czy mp3-kę to jedna z linii przesyłowych D+ jest podciągana w slave-a do plusa 3.3V. Przy podłączaniu master wychwytuje to podciągnięcie i zaczyna wysyłać do urządzenie różne zapytania np. kto ty jesteś, urządzenie odsyła mu vendora i jakiego rodzaju połączenie ma być interrupt, bulk itd. (endpointy) później zaczyna się instalowanie driverów np. następnie urządzenie podsyła kolejne parametry np. wydajność prądową i jakieś inne dane (nie pamiętam dokładnie). Po tych operacjach urządzenie ma już skonfigurowane endpointy i może rozpocząć normalną pracę. Ja zrobiłem jedynie na STM32 taki jakby konwerter USB->RS232 więc nie muszę żadnych driverów pisać a działa to dokładnie tak jakbym zwykły port RS232 obsługiwał. Niestety jakbyś chciał dokładnie dowiedzieć się jak działa master (host) to chyba trzeba by usb.org poczytać na temat działania usb a to niestety ciężka lektura. Życzę sukcesów pozdrawiam

    PS. Na linuchu da się w łatwy sposób odpalić tzw. monitor usb co i jak jest podsyłane, jak sobie myszkę podsłuchuję, jakby Cię interesowało jak to podsłuchać na ubuntu to daj znać.
  • Poziom 9  
    flapo213 wielkie dzięki za odpowiedź. W zasadzie wiem już co i jak ale rozwinę post, może kiedyś komuś się do czegoś przyda.

    Problem polegał głównie na tym, że normalnie gdy masz coś do odczytania od urządzenia np. przez port RS232 to generuje ono przerwanie i w przerwaniu możemy sobie odpowiednią maską sprawdzić co jest na rzeczy.

    USB w trybie bulk nie generuje (chyba) żadnych przerwań, tylko samo czeka na to, żeby odebrać dane od niego. Urządzenie powinno mieć jakiś bufor bo przecież skoro nie ma przerwań to nikt tak szybko nie nadąży z odbieraniem.

    Transfer do/z usb odbywa się asynchronicznie przy pomocy tzn. urb'ów. Wysyłasz urb do urządzenia a ono inicjuje transfer do nas lub od nas. Po wykonaniu transferu wywoływana jest funkcja complete, gdzie tego samego urb'a można zlecić do dalszego odbioru danych.

    Możemy również skorzystać z blokującej, synchronicznej funkcji usb_blk_msg, która sprawdza się wtedy gdy urządzenie jest obsługiwane przez jeden program kliencki lub dane wysyłane przez urządzenie nie muszą być współdzielone przez kilka programów klienckich. Żadna z tych sytuacji nie zachodziła w moim przypadku.

    Do transferu należy użyć urbów, które zapisują pobrane bajty od urządzenia do buforu sterownika a ten dopiero rozsyła je (właśnie w funkcji complete, wykonywanej po transferze) do buforów poszczególnych procesów. Procesy można przechować w polu private_data, w zdefiniowanej przez siebie strukturze, a identyfikować przez wskaźnik na struct file (z małej! Nie FILE z libc).
    Gdy już roześlemy bajty do procesów (co wydaje mi się można zlecić taskletom?) wysyłamy następny urb nadal w funkcji complete do kolejnego transferu.

    Start i stop urządzenia robimy w funkcji ioctl.



    Mam nadzieję, że komuś ułatwi to chociaż trochę życie :)
  • Poziom 21  
    Kolego almendar Twój opis jest super, ale tak tryb bulk rzeczywiście nie jest przerwaniowym trybem więc idzie kiedy ma czas a nie kiedy musi. Ale teraz nasuwa sie pytanie czy master który oczywiście musi mieć jakiś bufor nie odniera danych np po 512kb i nie żąda kolejnych - wiesz o co chodzi (żądam np 512kb i otrzymuję je czekam sobie i czekam np i wysyłam kolejen żądanie). Może zbyt banalnie to opisuję ale przyznam że nie wiem jak to dokładnie wygląda od strony mastera. Super post może się dobrze rozwinie i komuś jeszcze przyda. Napisz jak możesz czy dobrze to zrozumiałem.
  • Poziom 9  
    Źródła z których korzystam: ch13.pdf.
    Rozdział dotyczy bezpośrednio obsługi USB. Jeżeli kogoś interesują inne zagadnienia to niech w url zmienia numerek po chxx na taki z zakresu 01-18. Wszystko po angielsku, ale tekst jest pisany dość lekko i czyta się całkiem zgrabnie.

    Wracając do tematu, master ma bufor, który stworzy programista sterownika przy pmocy zwykłego kmalloc. Oto fragment kodu, który to ilustruje...
    Code:
     
    
    usb_fill_bulk_urb(urb, dev->udev,
                               usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
                               buf, writesize, adc22_write_bulk_callback, dev);
             urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
             usb_anchor_urb(urb, &dev->submitted);

             /* send the data out the bulk port */
             retval = usb_submit_urb(urb, GFP_KERNEL);

      (...)

             usb_free_urb(urb);


    Funkcja usb_fill_urb przyjmuje wskazanie na bufor (tu buf) oraz jego rozmiar (writesize). Tutaj też widać wskaźnik na funkcję callback wywoływaną po transferze. To w niej zlecić można kolejny transfer.

    Inaczej ma się sprawa w urządzeniu od którego odbieramy dane. To co teraz napiszę są to moje przypuszczenia. Sądzę, że bufor może w ogóle nie istnieć, urządzenie może np. wysyłać daną chwilową z momentu gdy nadeszła prośba o transfer. Tyle, że gdy urządzenie ma zadaną częstotliwość pobierania próbek to implikuje konieczność występowania buforu. Poza tym tryb bulk zapewnia pewną i bezbłędną transmisję. Po co taki typ transmisji skoro i tak nie zależało by nam na KAŻDEJ danej?
  • Poziom 31  
    Jesli moge wtracic jako osoba ktora niegdys zajmowala sie USB to stosowanie trybu bulk do urzadzenia jakim jest A/D jest troszke nie na miejscu (chyba ze nie interesuje cie ciagla zmiana sygnalu tylko np wartosc chwilowa). Twoje przypuszczenia sa sluszne jesli chodzi o tryb bulk to znaczy urzadzenie odpowiada w momencie w ktorym jest zapytane calym wczesniej zadeklarowanym w deskryptorze endpointu buforem wiec moze sie zdarzyc tak ze z punktu widzenia aplikacji czesc danych przepadnie jesli aplikacja nie nadarza z wysylaniem requestow. Problem taki bedziesz mial jednak zawsze nie wazne czy uzyjesz trybu isochronous czy interrupt czy bulk, wszystko zalezy od wielkosci bufora. Dodatkowo tryb bulk generuje oczywiscie przerwanie w ktorym nalezy bufor uzupelnic przed wyslaniem. Co do obslugi procesow to zlecanie kopiowania taskletom jest pomyslem niezlym ale nalezy sie zastanowic czy koniecznym (w koncu ile moze trwac zwykle kopiowanie z pamieci do pamieci ?). Co innego odczyt z dysku.
  • Poziom 9  
    Witam, wtrącenia od osób doświadczonych są nawet bardzo wskazane! :)

    Co do sensowności trybu bulk w tego typu rządzeniach nie mogę dyskutować. Płytka, która dostarcza mi próbki została dostarczona przez mojego prowadzącego w ramach projektu zaliczeniowego na studiach. Dostałem co prawda jej firmware oraz zezwolenie na dowolną modyfikacje, ale cóż nie mam ani programatora ani chęci w kopaniu się przez grube setki kodu. Taka była out-of-the-box i tak ustawioną programuje.

    Chociaż osobiście wydawało mi się od początku, że bardziej byłby sensowny tryb gdzie utrzymywany jest stały napływ próbek w zarezerwowanym z góry paśmie i nie koniecznie każda z nich jest tak ważna.

    Faktycznie zlecanie taskletów może mijać się z celem. Kopiowanie pamięć-pamięć jest szybkie. Przyszło mi to do głowy jako element dydaktyczny, którego mógłbym użyć, żeby nauczyć się ich poprawnej obsługi.

    Sprawa ma się tak, że to mój pierwszy sterownik i ruszyłem z tym 1,5 tygodnia temu. :) Wcześniej byłem kompletnie zielony z tego tematu i nowością było nawet jak kompilować i ładować taki sterownik. Na szczęście jest internet i bogata literatura do jądra Linuksa.

    Pozwolę sobie wrzucić link do jeszcze jednej strony:
    lxr.linux.no
    Okazała się ona świetną "przeglądarką" kodu jądra i nasunęła mi wiele gotowych rozwiązań moich problemów.


    Na koniec mam jeszcze pytanie do b. doświadczonych kolegów. Czy robienie prostych operacji zmiennopozycyjnych typu podzielenie dwóch liczb przez siebie raz na ileś jest b. kosztowne dla jądra?
  • Poziom 31  
    almendar napisał:

    Faktycznie zlecanie taskletów może mijać się z celem. Kopiowanie pamięć-pamięć jest szybkie. Przyszło mi to do głowy jako element dydaktyczny, którego mógłbym użyć, żeby nauczyć się ich poprawnej obsługi.

    Jako zadanie dydaktyczne napewno super.

    almendar napisał:

    Sprawa ma się tak, że to mój pierwszy sterownik i ruszyłem z tym 1,5 tygodnia temu. :) Wcześniej byłem kompletnie zielony z tego tematu i nowością było nawet jak kompilować i ładować taki sterownik. Na szczęście jest internet i bogata literatura do jądra Linuksa.

    W takim razie niezle ci idzie.

    almendar napisał:

    Na koniec mam jeszcze pytanie do b. doświadczonych kolegów. Czy robienie prostych operacji zmiennopozycyjnych typu podzielenie dwóch liczb przez siebie raz na ileś jest b. kosztowne dla jądra?

    Jezeli chodzi o jadro to jest troche bardziej kosztowne (wiekszy narzut na kontekst procesora o wszystkie rejestry zmiennoprzecinkowe) ale generalnie nie przejmowal bym sie tym zbytnio.