Hallo,
vielleicht eine ziemlich blöde Frage, weiß ich nicht. Aber ich stehe vor
einem (kleinen?) Problem.
Ich habe hier ein PCIe Dev Kit von Altera und soll u. A. Daten vom FPGA
an den PC senden. Nachdem ich mir die DMA Beispiele angesehen und die
Dokus gelesen habe ist meine Überlegugn nun, ob es nicht auch ohne DMA
geht.
Der ganze DMA Kram (zumindest in der vorhandenen Beispielanwendung) wird
im Design recht groß und ich steige da noch nicht ganz durch (kann sich
ja noch ändern).
Das Prinzip das hier benutzt wird (Daten vom PC in ein DMA Register im
FPGA schreiben, dieser wird dadurch gestartet und schickt die Daten aus
dem angegebene Adressbereich an den ebenfalls angegebenen Adressbereich
des PCs) klingt für mich simpel.
Nun ist die Frage, ob ich das auch ohne DMA (Controller) realisieren
könnte. Dann würde doc hschätzungsweise die CPU sich damit rumschlagen
müssen!? Wäre kein Problem. Die bestehende anwendung läuft auf einem
Pentium I, wird aber durch nen Core2 ersetzt. Was ich damit sagen will,
die CPU sollte dafür genügend reserven haben.
Ich kann bisher Daten in beide Richtungen senden (nutze bisher Windriver
von Jungo, war dabei, bin aber nicht der Softwareexperte und habe daher
bisher nur damit rumprobiert, allerdings scheint mir das ganze nur
bedingt flexibel zu sein). D. h. ich kann mit dem Windriver Daten ans
PCIe Board senden, dort wertet sie meine Logik aus und antwortet z.B.
darauf.
Nun würde ich halt gerne, nachdem der PC den Befehl gesendet hat,
größere Datenmengen (im niedrigen MB Bereich) an den PC senden.
DMA kam mir da natürlich als erstes in den Sinn, scheint mir aber recht
kompliziert zu implementieren zu sein.
Jetzt aber mal zu der eigentlichen Frage:
1. Ist es mein beschriebenes vorgehen (ohne DMA Controler) möglich? Also
Daten vom PCIe Board an Speicherbereiche des PCs zu schicken und diese
Dort (nachdem z.B. nen Interrupt ausgelöst wurde) auszulesen?
2. Wie reserviere ich für soetwas einen Speicherbereich am PC? Mei
nerster Gedanke was malloc(). allerdings wird dies wohl so einfach nicht
gehen, der Speicherbereich wird ja für das laufende Programm reserviert
und ja auch nur im virtuellen Speicher freigegeben.
3. Falls es so funktioniert wie ich es vor habe, wie sieht es mit
Geschwindigkeit aus?
4. Welche Alternativen hätte ich?
Vielleicht kann mir da ja jemand helfen, vielleicht bin ich ja auch
völlig auf dem Holzweg.
KDC
Hallo KDC,
also vorab, ich denke, wenn Du so etwas noch nie gemacht hast, brauchst
Du ca. 4 Wochen bis das alles läuft wie Du es haben willst. Vielleicht
etwas schneller wenn Du das alles mit Junko machen willst. Du brauchst
aber auf jeden Fall einen Gerätetreiber.
Zu Deiner eigentlichen Frage, ob Du 'einfach' vom End-Point ins PC
schreiben kannst, leider nein. Du schreibst ohnehin nie zum CPU sondern
der MEM Schreib- oder Lesezugriff von Deinem End-Point wird vom Chipset
(z.B. Intel ICH, I/O Controller Host) abgefangen und ins Systemspeicher
umgeleitet.
Nächster Punkt, es muss vorher ein Zielbereich im System Speicher
gelockt werden (sonst kriegs't einen schönen Blue Screen o.ä). Hier gibt
es verscheiden Ansätze. Hängt z.B. davon ab, ob Applikations (User Mode)
SW auch auf die Adressen zugreifen soll/darf oder nicht. So oder so
(User Mode oder Kernel mode only) muss dann die sog. Scatter/Gather
List, die den Zielbereich im Speicher beschreibt, an Dein DMA Controller
übermittelt werden. Dein HW kann dann diese Adressen verwenden.
Wenn Du Windows im Sinn hast, würde ich der umfangreiche WDK (Windows
Driver Kit) von der MS Webseite holen und ein sog. KMDF (Kernel Mode
Driver Framework) Treiber schreiben. Es gibt einige Beispiele auch für
PCI.
Wenn Du Linux im Sinn hast, ist m.E. die Information leider recht
verteilt. Ein guter Startpunkt ist der Rubini Buch, "Linux Devce
Drivers". Es gibt sogar irgendwo eine Online version (google, wikipedia
etc.).
Die Grundkonzepte zwischen Win und Linux sind recht ähnlich.
Zusammengefasst: Wenn Dein End-Point ein DMA Master sein will, brauchst
Du leider unbedingt einen Gerätetreiber. Ich denke bis das Fundament
steht hast Du schon so ca. 500 'C'-Codezeilen zu schreiben. Das Testen
kann nervig sein, vor allem unter MS.
Bzgl. des Durchsatzes ohne DMA. Das geht nur wenn die CPU auf Deine
Karte schreibt. Hier wirdst Du kaum über 2,5 MByte (typ. Response Zeit
von 1.6 us für ein DWord) beim Lesen bzw. 10 MB beim Schreiben kommen
(schneller weil keine Completions).
Viel Erfolg,
Charles
Hi Charles,
also erstmal zum Gerätetreiber:
Da nutze ich zur Zeit halt den Jungo Treiber, der auch DMA kann. Das
Dumme ist nur, dass Altera sowohl von der Hardware als auch vo nder
software her keinen Quelltext für die DMA Beispiele zur Verfügung stellt
bzw. gibt es für das einzige offene VHDL Beispiel keine spezielle
Software die mit dem Beispiel zurechtkommt.
Es ist so, dass ich das ganze für meine Diplomarbeit brauche. Bisher
habe ich wie gesagt den PCIe Core am laufen, kann auch Daten schreiben
(Memory Write Request) und Lesen (Memory Read Request + Completion).
Dabei komme ich tatsächlich auf geschwindigkeiten im niedrigen MB/s
Bereich.
Die Sache ist die, dass ich für den Treiber eigentlich gar nicht
zuständig sein sollte, das sollte ein anderer Diplomand machen.
Allerdings ist der noch nicht verfügbar. Die Anwendung soll im Endeffekt
utner Linux laufen.
Da ich aber im Endeffekt mit der Geschichte weniger zu tun haben
sollte/wollte, habe ich unter windows den Jungo Treiber verwendet, auf
dem basieren auch die Altera Beispiele und man brauchte helt nicht viel
machen und er lief out-of-the-Box mit einigen Beispielen (dummerweise
kein offenes DMA Beispiel).
Linux soll ja im prinzip vom Treiberschreiben einfacher sein. Da gibt es
auch einen OpenSource Treiber von einem anderen User, der aber nicht
vollständig ist. Könnte ich aber zur Not drauf aufbauen.
Zum DMA:
Das mit den Adressbereichen und dem freigeben ist mir schon klar, kann
ja nicht einfach mal eben irgendwelche wichtigen/benutzten
Speicherbereiche platt machen. Bluescreen ich komme! Meine Frage
diesbezügliche wäre ja, ob und wie ich die Bereiche für externe Zugriffe
freigeben könnte.
Ich habe das mit dem DMA bisher so verstanden: Ich schieb dem FPGA Infos
in sein Register (Quell Adresse, Ziel Adresse, Länge, etc.) und sag ihm
er soll den Transfer starten. Und er überträgt dann die Daten als Memory
Write Request mit der entsprechenden PC Adresse die Daten an den PC.
Mein Problem ist, dass ich mir gerne den ganzen Kram an DMA Logik im
FPGA sparen würde (sofern möglich) da ich nur in Richtung PC senden
möchte, und das auch "nur" 4MB und auch nicht in zerstückelte
Speicherbereiche sondern am Stück. Vier Wochen wären kein Problem ^^
Meine Frage ist halt, ob/wie ich den Speicherbereich am PC freigeben
könnte, ob der Ansatz so funktioniert (also im Endeffekt nur Daten vom
FPGA aus an die Speicherbereiche am PC schicken, die Bereiche hab ich
dem FPGA natürlich vorher mitgeteilt) und welche Geschwindigkeit dabei
zu erzielen ist.
Ich hab mit mit dem "Software" Logic-Analyzer halt das angesehen, was
das DMA Beispiel im DMA Transfer macht, und das war nur TLPs (Memory
Write Req) senden.
Hallo,
Ich glaube Du haust da zwei Sachen drucheinander,
wenn dein PCIe Core einen Memorywrite initiert IST das ein DMA. Da
beisst sich Maus keinen Faden ab.
Ich glaube wo Du dich vor fürchtest ist Scatter/Gather sprich eine Liste
von (4kB) Blöcken abzuarbeiten, welche Dir der Treiber zur Verfügung
stellen muss...
Scatter/Gather ist sicherlich guter Stil, jedoch nicht unbedingt
notwendig. Wenn Du einen Treiber baust der Dir bei Systemstart einen
entsprechend grossen Speicherbereich alozierst, dann bekommst Du den (im
Normalfall) auch am Stück zur Verfügung gestellt.
Du musst Dir dann noch die physikalische Adresse zu deinem
Speicherbereich geben lassen, dafür gibt es Betriebsystemfunktionen.
Der rest läuft dann wie Du schon gesagt hast, über einfache Writes.
Die zu erzielende Geschwindigkeit ist dabei höher als bei
Scatter/Gather, da Dir du keine Zeit zum Übertragen von Tabellen
benötigst.
Gruß
Andreas
Hi KDC,
mir ist immer noch nicht ganz klar wie Eure gesamte Arbeit ausschauen
soll. Kommt dann noch eine Applikation SW dazu, die die Daten irgendwie
visualisieren/auswerten soll oder was macht Ihr damit.
Denn, normalerweise wird der Datenverkehr immer von einer
Applikation-SW, auch natürlich Demon, Dienst o.ä, eingeleitet. Dein
Hardware kann von sich aus eigentlich nur über Interrupts eine
Betriebssystem aktivität auslösen aber da wird es richtig aufwendig.
Falls Dein HW Daten im Speicher sozusagen auf Vorrat halten soll, dann
wird in der Regel ein Treiber vorher einen Speicherbereich reservieren
(z.b. kmalloc) und die Adresse von diesem Puffer in die HW schreiben.
Das Gesamtsystem würde aber eher so ausschauen.
Eine Applikations-SW (auch Demon natürlich) hat z.B.
fH = open("/dev/irgendwas", PARAMS);
BytesRead = read(fH, buf, sizeof(buf));
Das System weiss welcher Treiber sich hinter "/dev/irgendwas" verbirgt.
Dein Treiber hat einen Callback vereinbart der vom System aufgerufen
wird als Folge des 'read' Befehls oben. Als Parameter bekommt er einen
Zeiger auf 'buf', einen Puffer im User Space.
Der Treiber reserviert dann normalerweise Kernelspeicher (kmalloc) und
übergibt diese Adresse an Dein HW. Wenn der HW signalisiert hat, dass er
fertig ist (Interrupt, Polling eines Status Regs etc. etc.) kopiert der
Kernel die Daten nach '*buf' mit copy_to_user() und der read() Befehl
wird abgeschlossen.
Was ich zeigen will ist, ein DMA Treiber ist alles andere als trivial.
Meine persönliche Meinung ist, wenn der Treiber eh von einem Komilitonen
geschrieben werden soll und Dein Thema eigentlich der HW ist,
konzentriere Dich doch lieber auf einer vernünftigen Simulation für Dein
Konzept. Die Altera Umgebung ist doch diesbzgl. gar nicht so schlecht.
Die gewünschte Übertragungsrate wirdst Du auf jeden Fall im Zielsystem
erreichen. Mit DMA kommst Du problemlos in Richtung 100 MByte vielleicht
sogar bis hinauf zu ca. 180 MByte oder so (x1 PCIe). Letzendlich muss
nur der Treiber Dein HW schnell genug mit Zieladressen versorgen.
Ein Vorschlag für Dein DMA Controller wäre ein 64bit x N
Zieladressen-FIFO, der vom Treiber periodisch gefüllt wird. 64 Bit weil
48 Bit Start-Adresse (IA64 Hardware) + Grösse des Zielbereichs. Du
spezifizierst dann Deinen Komilitonen was die maximale Bereichsgrösse
ist, die Du bearbeiten kannst (z.B. 32 KByte oder 64 KByte). Dein
Controller holt sich dann sequentiell Ziel-Deskriptoren aus diesem FIFO
und beschreibt sie mit posted PCIe Pakete. Du brauchst natürlich auch
ein Signalisierungsmechanismus wenn ein Bereich abgeschlossen ist
(Interrupt, Register Polling, Stempel an eine andere Speicheradresse etc
etc.)
Nochmals, viel Erfolg
Charles
Danke ihr beiden!
Das hat mir schon einiges gebracht, in dem es mich im Endeffekt
bestätigt hat, dass meine grundsätzliche Idee nicht ganz bescheuert war
;)
Die Anwendung gibt im Endeffekt gelegentlich (natürlich von einer
Software gesteuert) Befehle an eine Logik, die damit einen Testchip
beschaltet. Irgendwann ist der Punkt erreicht, wo der Speicher im
Testchip ausgelesen werden soll. Das sind maximal 4 MB.
Daher reicht es mir im Endeffekt, Daten als normalen Memory Write
Request an das FPGA zu schicken, und halt nach einiger Zeit(dann aber
halt auch möglichst schnell) die Daten aus dem Chip Speicher zu lesen.
Wie gesagt, der Rest funktioniert soweit ganz gut, ich würde jetzt halt
nur gerne den Abtransport der Daten realisieren. Dazu nochmal meine
überlegungen:
1. PC reserviert Speicher.
2. PC Teilt FPGA die Adresse des reservierten Speichers mit
3. FPGA sendet die Daten (sofern PCIe Core sagt es sei Ready)
hintereinander weg an die (natürlich mit Inkrementierung) erhaltene
Speicheradresse
4. Wenn das FPGA fertig ist sendet es eine MSI mit Interrupt an den PC
5. Wenn der PC den Interrupt empfängt (alternativ Polling) holt die
Software sich die Daten und wertet sie aus/speichert sie in eine Datei
Das war im Prinzip so, wie ich es mir vorgestellt hatte, und wie ihr es
auch beschrieben habt, oder?
Dann noch zwei kurze Fragen dazu:
1. Wie groß können die reservierten Speicherbereiche sein?
2. Wenn ich es z. B. mit kmalloc mache, habe ich dann die tatsächliche
physikalische Adresse? Oder wie komme ich da dran? Und der Chipsatz/PCIe
Bus kann einfach in den Speicherbereich schreiben auch wenn ich ihn (in
der anwendung) reserviert habe? Reservierung ist also nur für das
Betriebsystem?
Zum Thema Simulieren: Ja, von Altera gibt es da Simulationswerkzeuge,
wobei mir eigentlich der praktische Test immer lieber ist (zumindest bei
so einer Anwendung).
Hi KDC,
Deine Punkt 1) bis 5) sind so in Ordnung. Nur nebenbei, MSI ist aus PCIe
sicht kein Interrupt sondern ein Memory Write an eine vom System
vorgegebene Adresse. Der 1 DWord Payload wird, bis auf den unteren acht
Bits (der Vektor), auch vom System vorgegeben. Noch ein Punkt:
Windows-XP (und früher) kann kein MSI. Ach ja noch ein Punkt, wenn Dir
PCIe Kompatibilität im Sinne der Spec. ein Anliegen ist, musst Du auch
Legacy Interrupts (INTA bis INTD Messages) unterstützen, wenn Du
Interrupts implementierst. Das System 'entscheidet' dann welches Du
benutzen darfst und trägt das in die Konfiguration Space ein.
kmalloc() reserviert physischen Speicher und zwar bis zu einer maximalen
Größe von ca. 128 KByte (ja, Kilo). Für größere Bereiche gibt's
vmalloc() aber dann musst Du (genauer gesagt Dein Treiber Kollege)
letzendlich eine Scatter/Gather Liste der physischen Adressen anfordern
und dies dann an den FPGA übertragen. vmalloc() bekommt einen virtuellen
Speicherbereich. Ich denke, wenn Du ein S/G-Listen FIFO in Deinem DMA
Kanal implementierst, bist Du hier sehr flexibel auch gegen späteren
Änderungen im Treiber Konzept. Der Treiber muss Dein HW nur mit Adressen
versorgen. Es ist dann egal, ob der Treiber mit einem grossem
(virtuellen) Puffer, mit Wechsel Puffer oder noch was arbeitet. Dein HW
arbeitet natürlich mit physischen Adressen.
Das mit dem Simulieren verstehe ich. Auch ich sehe natürlich lieber
persönlich nach, ob etwas 'blinkt und rappelt' nur ab einer gewissen
System Komplexität ist das leider nicht mehr effektiv. Vor allen die
Fehleranalyse kann uferlos sein. M.E. hast Du diese Komplexitätsschwelle
schon überschritten. Ich würde wenigstens zweigleisig fahren.
Grüße,
Charles
Das MSI kein Interrupt ist war mri schon klar, gibt je keine weiteren
Signalleitungen sondern geht alles über die eine tx Leitung, somit muss
das ja quasi ein normales Datenpaket mit speziellem Inhalt sein. Aber
danke nochmal für den Hinweise.
Genauso dass XP kein MSI kann, hatte ich irgendwo schon gelesen, aber
wieder verdrängt.
Ich bin grad dabei mich mal ein wenig mit Linux auseinander zu setzen.
Es gibt ja dieses Buch aus dem O'Reilly Verlag welches kostenlos als pdf
zur Verfügung steht über Linux Treiber.
Da steht übrigens drinne, dass mit free_pages (oder so) und order=9
irgendwas um die größenordnung 512kB möglich sein soll. Bei so großen
Bereichen muss man ja aber sicherlich drauf aufpassen, ob die wirklich
allociert werden können wenn sie am Stück zur Verfügung stehen müssen.
Da gehts dann vielleicht, wie du ja auch vorschlägst, Richtung
Scatter/Gather. Muss ich mir mal ansehen.
Und nochmal zur Simulation:
Ich hab bisher nur einmal diesen Altera BFM verwendet und ein Beispiel
zu testen. Ist das wirklich viel effizienter das darüber zu machen als
mit SignalTap sich einfach z.B. den Datenverkehr und einige andere
Signale anzusehen? Wie gesagt, Simulieren habe ich bisher
vernachlässigt, nur um einzelne Logik Blöcke zu testen habe ich das
benützt.
Ein paar Fragen sind so heute beim lesen noch aufgetaucht:
1. Wie bekomme ich unter Windows nen zusammenhängenden physikalischen
Adressbereich und dessen Adresse?
2. Kann ich per Memory Write Request ohne sonstige Einstellungen in den
Speicher schreiben (vom FGPA aus) oder muss ich am PC dafür noch
irgendwas vorbereiten? (ausser Bereich freigeben)
3. Wenn ich am PC in den Speicherbereich vom PCIe Board schreibe(also z.
B. das erste Byte von Bar0) wird dass dann vom Root Complex automatisch
in ein TLP umgewandelt oder muss ich da selber aktiv werden.
Ich glaub das wars. Windows deswegen, weil ich da halt mit Windriver von
Jungo nen einfaches Toolkit habe, womit ich auch schon kleine
Testsoftware geschrieben habe und was für mich, der sich eigentlich
nicht um die Treiberseite kümmern soll, wesentlich simpler und
unaufwendiger ist. Die dritte frage richtet sich aber doch eher an
Linux, damit hab ich heute rumgespielt, aber leider kein Modul geladen
bekommen :/
Hi KDC,
mit den 512 KB wäre ich tatsächlich vorsichtig. Ich kenne nur die
Angabe, dass kmalloc() bis 128 KByte spezifiziert ist. Kann schon sein,
dass neuere Kernel Versionen diese Zahl noch mal erweitert haben. Habe
ich mich ehrlich gesagt nicht drum gekümmert. Ich verwende meistens
vmalloc(). Wenn kmalloc() nicht die gewünschte Anzahl zurückliefern
kann, liefert er meines Wissens keine (retcode = 0).
Als Vergleich, unter Windows habe ich mal in einen Treiber ein Tracer
eingebaut, der die Größe der einzelnen S/G-List Elementen ausgegeben
hat. Gleich nach dem Laden habe ich 4 mal 20 MByte angefordert (für 4
DMA Kanäle). Ich habe nie gesehen, dass ein S/G Element größer als 3
Seiten (3 x 4K Byte) war. Ich bin noch nicht dazu gekommen, die Messung
unter Linux zu wiederholen.
Bzgl. der Simulation, das Problem mit dem SignalTap ist, dass Du nur ein
oder wenige Übertragungen anschauen kannst. Mit der Simulation, kannst
Du z.B. eine ganze Regression Sequenz übers Wochenenende laufen lassen.
Es ist eine Weile her seitdem ich mir das Altera BFM angeschaut habe.
Damals zumindest war es auf verilog bassiert, sah aber sehr vernünftig
aus. Ich hoffe, dass Dein Simulator mixed language (VHDL/Verilog) kann.
Grüße,
Charles
@KDC
2.) ohne IOMMU nicht, also generell nicht
3.) ja
Passend fuer die Anwendung ist
pci_alloc_consistent
wenn man frueh nach dem booten alloziiert bekommt man auf jeden Fall 2
MB.
Bei entsprechender Fragmentierung bekommt man aber nicht mehr als 4kB.
(bzw. eine page)
Danke euch beiden für die Antworten! Wobei ich mir jetzt bei Frage drei
nicht sicher bin mit deiner Antwort. Ich mach sowas auch, aber auf
"oder-Fragen" mit ja antworten ist immer etwas unglücklich ;) aber ich
nehme mal an du meinst er macht daraus automatisch nen TLP (sonst
hättest du ja evtl. noch mehr dazu geschrieben).
Wie gesagt, danke euch allen, das einzige was ich leider immer noch ned
wei ist, wie ich bei Windows an freien physikalischen Speicher kommen.
Aber das lässt sich ja hoffentlich rausfinden.
KDC
@KDC
Ja, hab die Frage nicht ganz gelesen, wird natuerlich automatisch
umgewandelt.
BTW. ich wuerde an deiner Stelle den Treiber selbst schreiben und auch
etwas testcode, denn Du weisst nicht wielange es dauert bis der andere
Diplomand mit seinem Zeug voran kommt.
Mach daraus einfach noch ein Kapitel in deiner DA.
Hi KDC,
wie Du unter Windows an physischen Speicher kommst ist aus dem Stand
nicht so einfach.
Aus dem User Mode (d.h. Borland, VisualXxx usw.) geht das überhaupt
nicht. Windows versucht verzweiflet den unbedarften vor sich selbst zu
schützen. Am Anfang hast Du erwähnt, Du würdest mit Jungo arbeiten.
Falls Jungo die Einbindung von Funktionen aus der Windows WDK erlaubt,
würde ich versuchen Speicher in der gewünschten Größe aus dem Kernel
non-paged Pool anzufordern. Aber 4 MByte ist schon ziemlich Richtung
Obergrenze für Speicher Blöcke, die aus dem Pool angefordert werden
sollten.
Der Aufruf wäre ExAllocatePoolWithTag(type, nbytes, tag)
type : NonPagedPool
tag : Ein Bezeichner, wenn ich mich recht errinnere, max 6 Zeichen
und
vermutlich auch noch als UNICODE string. Ich muesste
nachschauen.
Rückgabe Wert ist ein Pointer auf eine Virtuelle Adresse. Du kannst
MmGetPhysicalAddress(PVOID baseAddr) verwenden um die physische Adresse
zu bekommen.
Nachdem ich das gesagt habe, muss ich leider auch gleich sagen,
Microsoft weisst darauf hin, dass diese Methode nicht für DMA verwendet
werden soll. Ich habe es nur erwähnt, weil es zusammen mit Jungo
funktionieren könnte (nie probiert, habe kein Jungo).
Wenn man es 'richtig' machen will, wird es wirklich kompliziert. Vergiss
nícht, ich habe schon am Anfang von ca. vier Wochen gesprochen. Wenn Du
wirklich ein eigener Bonsai Treiber schreiben willst, damit Du z.B. den
TLP Inhalt in einem Borland/VisualXxx Program sehen möchtest, hast Du
einiges vor Dir.
Als erstes, WDK und SDK von der Microsoft Seite laden. Da ist die ganze
Docu drin und viele viele Beispiele.
1) Für Dein Treiber (hier empfehle ich dringendst das KMDF Treiber Model
(s. WDK), alles andere ist viel zu kompilziert) brauchst Du einen
Registry Eintrag, damit Deine Applikations SW es überhaupt finden kann.
Hierzu brauchst Du einen sog. GUID (globally unique Id). Die wird mit
GENGUID.exe aus der SDK generiert. Den String hieraus kommt als
'#DEFINE' in Deinen Treiber, Deine Applikation SW und Deine *.INF Datei
Dein Treiber wird übrigens immer mit dem WDK C-Compiler in einem DOS Box
übersetzt. VisualStudio usw. kann das nicht.
2) In der Applikations Software (Borland/VisualXxx) verwendest Du
SetupDiGetClassDevs()
SetupDiEnumDeviceInterfaces()
SetupDiGetDeviceInterfaceDetail()
um einen Handle zu Deinem Treiber aufzubauen. (s. Beispiel in der SDK)
3) Mit
CreateFile()
ReadFile(drvHandle, buf,....)
kannst Du dann Daten von Deinem Treiber abholen.
---------------
Nun zum Treiber:
1) genauso wie unter Linux musst Du letzendlich einige Callbacks
vereinbaren. Vor allem,
DriverEntry()
EVT_WDF_DRIVER_DEVICE_ADD
EVT_WDF_DEVICE_PREPARE_HARDWARE
In Deiner EVT_WDF_DEVICE_PREPARE_HARDWARE erfährst Du, wie Deine PCI
Base Address Register gemapped sind
2) Mindestens ein IO-Queue Anlegen. Das Betribssystem leitet Deine
ReadFile() Aufrufe hierhin. Die KMDF bietet dann einige Aufrufe um Queue
Einträge abzuholen. Oder genauer, KMDF ruft Deine Callbacks auf, wenn
Queue Einträge vorhanden sind.
3) Aus Deinem Queue Eintrag (IRP, I/O Request Packet in Windows Jargon)
musst Du nun Zugang zum 'buf' aus Deinem ReadFile bekommen (am besten
den sog. DMA_DIRECT Methode verwenden. Auch hier s. KMDF Docu)
Mit
WdfDmaTransactionInitializeUsingRequest(..CallbackFuerDma()...)
WdfRequestRetrieveOutputBuffer()
wird im Wesentlichen Deine DMA Übertragung gestartet.
Deine Routine CallbackFuerDma(), bekommt dann die Scatter/Gather Liste
zu Deinem 'buf' im User Space vom System mitgeteilt und diese Adressen
schreibst Du in Dein Hardware.
4) Mit WdfRequestComplete() wird das ganze abgeschlossen.
Das hier sind eigentlich nur als Stichwörter/Orientierungspunkte gemeint
für Dein Studium der WDK Beispielen. Es lässt sich wirklich nicht
umfassend auf nur einer HTML Seite beschreiben. Ab besten, den PLX 9x5x
bzw. pcidrv Beispiele aus der WDK anschauen. Rechne, wie gesagt, mit
einigen hundert Code Zeilen, viele Blue Screens und natürlich vier
Wochen.
Grüße,
Charles
cfgardiner schrieb:
> Rechne, wie gesagt, mit> einigen hundert Code Zeilen, viele Blue Screens und natürlich vier> Wochen.
Nicht zu vergessen das Serial oder FireWire Kabel sowie ein 2. Rechner
für den Kernel-Debugger :)
Hi Christian,
ach für einen Bonsai Treiber braucht man das alles eigentlich nicht,
auch wenn es MS vorschreibt. Klar man kommt vielleicht damit etwas
schneller ans Ziel. Sofern man sich z.B. mit dem ganzen PnP Zeug nicht
auseinander setzen muss, kann man schon darauf verzichten.
Wen ich z.B. für einen Demo oder auch kleineres Projekt o.ä einen
Treiber schreibe, mache ich immer nur folgendes:
- Compilere mit Debug
- Verify aktivieren
- Nach einem Reboot, Crash Mini-Dumps mit WinDbg analysieren.
Die angemeckerte Zeilen sind meistens in der Nähe vom tatsächlichen
Fehler, zumindest sofern man mit dem KMDF Model gearbeitet hat.
Klar, alles Abwägung.
Grüße,
Charles
Ich seh schon, zumindest nen komplett eigener Treiber droht mir überden
Kopf zu wachsen. Bei Linux bekomme ich nicht mal ein Modul
compiliert/geladen und an Windows will ich lieber gar nicht denken ;-)
Nunja, der Jungo Treiber hat eine funktion um Speicher für DMA zu
reservieren, allerdings weiß ich nicht ob die für meine Zwecke
funktioniert, das werde ich dann mal als nächstes testen.
Dann habe ich gehört, dass es hier im Haus auch schon ne
treiberentwicklung gab, für eben dieses Board aber eine anderen
Anwendung. Da bin ich auch dran mit den Code mal zu besorgen. Ansonsten
muss doch Linux herhalten.
Ich schau mal was sich machen läßt.
So,
ich wollte mich nochmal bei allen bedanken, die mir geholfen haben.
Bin zwar was den DMA Teil angeht noch nicht fertig, aber das Prinzip
funktioniert soweit super!
Und um noch ein bisschen Wissen beizusteuern:
mit __get_free_pages(GFP_KERNEL, 10) läßt sich unter Linux im Treiber
ein zusammenhängender Speicher von 4 MB reservieren. Der aktuelle Kernel
kann meines Wissens auch noch order (ist das zweite Argument, 2^n * 4kB)
11, also 8 MB.
GFP_KERNEL geht in meinem Falle, da mein PCI (Express) Board 64 bit
Adressen verwalten kann, falls das nicht geht muss man es um __GFP_DMA
ergänzen, damit ist bei mir aber dann die zweite oder dritte Allozierung
schief gegangen.
So, wie gesagt, vielen Dank euch allen nochmal!
KDC