Hallo Zusammen,
ich bin gerade dabei meine ersten Kernel Module zu schreiben, und hätte
einige Fragen auf die ich leider per Recherche keine Antwort finde
konnte.
Ich weiß auch nicht ob ich hier 100% richtig bin, leider kenne ich keine
deutschsprachiges Embedded Linux Forum :)
Da ich aber für viele Einsteigerfragen Rund um den STM32 hier gute Infos
gefunden habe, werfe ich meine Fragen mal hier in die Runde, und hoffe
auf jemanden der sie beantworten kann.
Was ich eigentlich vorhabe:
Ich habe ein STM32F3 Discovery Board, auf diesem läuft ein SPI-Slave,
über einfaches Protokoll können die LEDs auf dem Discovery Board ein und
ausgeschaltet werden. Für diese Ansteuerung möchte ich einen Kernel
Treiber erstellen.
Prinzipiell funktioniert das ganze auch schon. Auf meinem Embedded Board
(BeaglboneBlack), aus dem Userspace, per spidev, kann ich die LEDs auf
dem STM Board ansteuern.
Nun habe ich mich daran gemacht das ganze als Kernel Treiber zu
realisieren.
Ein einfaches Kernelmodul, welches beim Initialisieren ein paar LEDs auf
dem STM Board blinken lässt war auch recht schnell erstellt.
Nun kommen wir zu meinen eigentlichen Frage:
Ich möchte das aus dem Userspace, ähnlich wie bei der Verwendung mit
spidev, auf das Treibermodul zugegriffen werden kann.
Dafür kommt soweit ich weiß unter Linux ein "Character Device" in Frage.
Muss ich nun dafür in mein Treibermodul ein eigenes "Character Device"
implementieren?
Oder gibt es evtl. eine andere Möglichkeit aus dem Userspace auf ein
"SPI-Device" zuzugreifen?
Ich habe mit innerhalb des Linux Kernels den Quellcode für den MAX7301
(gpio-max7301.c) angesehen (und ein paar weitere).
Dabei habe ich gesehen das diese Lese- und Schreibfunktionen
bereitstellen:
Leider war ich nicht in der Lage herauszufinden "wer" diese Funktionen
den eigentlich aufruft.
Ich vermute irgendetwas aus dem Linux SPI-Treiber Subsystem, aber eine
richtige Antwort konnte ich nicht finden.
Evtl. wäre das auch ein Punkt an dem ich ansetzen könnte.
Hat jemand dazu eine Idee?
Vielen Dank für alle Antworten im Vorraus.
Gruß,
Sebastian (seho85)
Warum muss das unbedingt ein Kernelmodul werden?
User-Space Programme lassen sich einfach kompilieren und kopieren. Auch
per Crosscompiler Da kann auch die Linux-Version darunter mal gewechselt
werden(neuer Kernel)
Bei Kernel-Treiber müssen diese immer mit dem Kernel übereinstimmen. Und
auch die passenden Kernel-Header müssen beim Kompilieren vorhanden sein.
Nach einen Kernel-Upgrade geht dann evtl nichts, da die Versionsnummer
verglichen wird.
Ich bin von Kernelmodulen weg, und mache alles im User-Space. Auch SPI
Kommunikation.
seho85 schrieb:> Leider war ich nicht in der Lage herauszufinden "wer" diese Funktionen> den eigentlich aufruft.
ts->read und ts->write in gpio-max730x.c.
> Muss ich nun dafür in mein Treibermodul ein eigenes "Character Device"> implementieren?
Nein. Siehe Documentation/spi/spi-summary.
Hi,
danke für die Antworten.
Irgendwie muss ich wohl was an den Augen gehabt haben...
Vielen Dank für den Hinweis.
Mir nun ist nun im groben klar wie die der Zugriff funktioniert.
Die Funktionen werden letztendlich über das Linux GPIO Subsystem
aufgerufen.
>> Muss ich nun dafür in mein Treibermodul ein eigenes "Character Device">> implementieren?>Nein. Siehe Documentation/spi/spi-summary.
Ich habe das spi-summary nochmals gelesen - Ich verstehe nur leider
immer noch nicht wie auf mein eigenes SPI Device dann aus dem Userspace
zugreifen kann.
Kannst du mir da bitte einen weiteren Hinweis geben?
>Bei Kernel-Treiber müssen diese immer mit dem Kernel übereinstimmen. Und>auch die passenden Kernel-Header müssen beim Kompilieren vorhanden sein.>Nach einen Kernel-Upgrade geht dann evtl nichts, da die Versionsnummer>verglichen wird.
Dies ist momentan kein Problem - Kernel sowie Kernelmodul mithilfe von
yocto erstellt. Upgrades für Kernel Module und Software kommen alle aus
dem yocto system.
Oder übersehe ich bei dieser Annahme etwas?
Generell ist das ein Lernprojekt für mich.
Nochmals vielen Dank für die Antworten.
Gruß Sebastian
Morning,
kennst Du schon dieses Buch: https://lwn.net/Kernel/LDD3/ ? Eine sehr
gute Einführung, die genau diese Art von Fragen zu beantworten versucht.
Das Buch gibt es meines Wissens nach auch in der deutschen Übersetzung,
ob die kostenlos zum Download zur Verfügung steht, kann ich aber nicht
sagen.
Hallo PittiJ,
über einen Aufruf in dieser Art habe ich auch schon nachgedacht.
Nur welchen konkreten "DevicePath" muss denn angeben um auf mein SPI
Device zu zuzugreifen?
Verwende ich z.B. spidev, habe ich zumindest den Eintrag unter
/dev/spi<major>.<cs>
Mein eigener Treiber stellt mir so etwas nicht bereit - zumindest
solange ich nicht explizit ein "characterdevice" in den Treiber
implementiert habe.
Vielleicht übersehe ich nur irgendetwas....
>kennst Du schon dieses Buch: https://lwn.net/Kernel/LDD3/ ? Eine sehr>gute Einführung, die genau diese Art von Fragen zu beantworten versucht.>Das Buch gibt es meines Wissens nach auch in der deutschen Übersetzung,>ob die kostenlos zum Download zur Verfügung steht, kann ich aber nicht>sagen.
Danke für den Tipp, ich werde mich dort mal hineinlesen.
Ich verwende folgendes Buch:
https://www.dpunkt.de/buecher/12176/9783864902888-linux-treiber-entwickeln.html
Leider ist das Thema SPI innerhalb dieses Buches nur kurz Umrissen, es
wird erklärt wie man ein SPIDevice selbst erstellt, und wie man per
"spidev" aus dem Userspace SPI Kommunikation betreiben kann.
Leider fehlt mir die Information wie man diesen SPI Device Treiber
konsumiert.
Gruß,
Sebastian (seho85)
seho85 schrieb:> Ich habe das spi-summary nochmals gelesen - Ich verstehe nur leider> immer noch nicht wie auf mein eigenes SPI Device dann aus dem Userspace> zugreifen kann.
Dein Treiber erzeugt beim Laden eine Datei (zB. /dev/myCoolSpiDevice),
die ein character device repräsentiert. Dein Userspace-Programm öffnet
diese Datei dann r/w, schreibt Deine Befehle hinein und liest die
Antworten des SPI-Geräts daraus.
Als Beispielcode für das Kernelmodul kannst du den Code von spidev.ko
benutzen. Beispielcode für Dein Userspace-Programm findest Du unter
Documentation/spi/spidev_test.c in Deinem Kernelsource-Verzeichnis.
schreibe per spi_write([lokales struct spi_device], pBuffer, nByte)
10
}
Ist das denn Grundsätzlich die richtige Herangehensweise? Der Treiber
muss "selbst (per code)" das Character Device erstellen?
Per "echo >" und cat liefert der Treiber mir die erwarteten Werte.
Ich werde mir den originale spidev Quellcode mal etwas genauer ansehen.
Ich habe mich zulange mit diesem Kommentar beschäftigt:
>> Muss ich nun dafür in mein Treibermodul ein eigenes "Character Device">> implementieren?>Nein. Siehe Documentation/spi/spi-summary.
Daraufhin habe ich nach einem anderem Weg gesucht auf meinen Treiber
zuzugreifen. Scheinbar 'ne blöde Idee :D
Gruß,
Sebastian (seho85)
seho85 schrieb:> Ist das denn Grundsätzlich die richtige Herangehensweise? Der Treiber> muss "selbst (per code)" das Character Device erstellen?
Ja, genau so.
> Per "echo >" und cat liefert der Treiber mir die erwarteten Werte.
Prima.
> Daraufhin habe ich nach einem anderem Weg gesucht auf meinen Treiber> zuzugreifen. Scheinbar 'ne blöde Idee :D
Das folgt der UNIX-Philosophie "alles ist eine Datei", oder anders
gesagt: Hardware wird durch "special files" ("device files" oder
"Gerätedateien") im Verzeichnis /dev repräsentiert. Über diese Dateien
wird dann die Hardware angesprochen.
Früher wurden solche Gerätedateien noch fest mit mknod(1) angelegt, und
zwar unabhängig davon, ob die dadurch repräsentierte Hardware überhaupt
im System vorhanden war. Dadurch hatte das /dev/-Verzeichnis damaliger
Linux-Versionen viele überflüssige Einträge für nicht vorhandene
Hardware; obendrein machte das System Probleme, wenn Geräte wie zB.
USB-Hardware dynamisch zur Laufzeit angeschlossen wurden.
Deswegen haben die Kernelentwickler mit Linux 2.2 dann zunächst das
devfs entwickelt, welches viele der Nachteile des vorherigen statischen
Systems behoben hat, aber immer noch nicht der Weisheit letzter Schluß
war. Heute benutzen moderne Linux-Systeme "udev", das mittlerweile auch
in das neue Init-System systemd integriert wurde.
Nochmals danke für deine Ausführungen.
Ich habe die "Character Device Vorlage" aus dem "Linux Treiber
entwickeln" Buch übernommen. In der Beschreibung stand auch das udev in
dem Beispiel verwendet wird.
Nun werde ich mich mal an den "NON BLOCKING" Zugriff machen.
Das procfs wollte ich mir auch mal näher ansehen.
Wenn das geschafft ist werde ich mir mal das "LED Subsystem" ansehen,
und schauen ob ich ein paar LED auf dem STM32 Board als Netzwerk Tx
Signal nutzen kann.
Dabei gibts dann bestimmt noch die eine oder andere Frage :D
Gruß,
Sebastian
seho85 schrieb:> Was ich eigentlich vorhabe:> Ich habe ein STM32F3 Discovery Board, auf diesem läuft ein SPI-Slave,> über einfaches Protokoll können die LEDs auf dem Discovery Board ein und> ausgeschaltet werden. Für diese Ansteuerung möchte ich einen Kernel> Treiber erstellen.
Hallo Sebastian,
Also du hast ein SPI Device was einige LEDs ansteuern kann.
Wenn es ein kaufbares Device ist und nicht was selbst hergestelltes,
warum benutzt du nicht den DeviceTree als "Beschreibungssprache" und das
Linux LED Interface. Wird in deinem Fall dann irgendwie als led-gpio
oder was in /sys/devices auftauchen.
Zu Test brauchst du den DeviceTree nicht
Wie das mit den LEDs genau funktioniert habe ich mir noch gar nicht
angeschaut.
Wo du den DeviceTree anspricht, fällt mir auch wieder ein das da was mit
LEDs war. Den wollte ich mir sowie nochmal im Zusammenhang mit meinem
Device Treiber ansehen (meinen Treiber per FDT einbinden).
Ich hatte erst mal vermutet das ähnlich wie bei GPIO Subsystem ist. Über
das GPIO Subsystem bin ich "gestolpert" als ich paar Device Treiber von
GPIO Portexpandern angesehen habe.
Das STM32F3 Discovery hatte ich hauptsächlichen wegen den 8 Onboard LEDs
verwendet und weil ich kein anderes SPI-Slave zur Verfügung Device
hatte.
Im Nachhinein viel mir dann auf des eigentliche eine gute Idee ist den
STM32 zu nehmen, da ich ihn als universelle Peripherie einsetzen kann.
SPI, I2C, UART, usw.
Ich bin sicherlich auch kein Profi für den STM32, aber durch CubeMX und
OpenSTM32 gepaart mit ein bisschen Übung ist es ja nicht mehr sonderlich
schwer ein lauffähiges Programm hinzubekommen.
Zumindest zu meinen "Bastler Anforderungen" :)
Ursprünglich kam ich durch Embedded Linux und yocto zu den Kernel
Modulen.
Nun werde ich aber doch erstmal versuchen meinem BeagleBone zu überreden
per NFS zu booten. Das läuft noch als neben Task.
Gruß,
Sebastian
Warum soll Dein character device denn überhaupt die read- und
write-Funktionen implementieren? Der von PittyJ skizzierte Weg über
ioctl ist doch wesentlich sinnvoller, und zwar aus den folgenden
Gründen:
- Bei SPI werden immer gleichzeitig Daten gesendet und empfangen. Daher
kann man read und write nicht separat betrachten, sondern würde z.B.
durch write einen Transfer anstoßen und die gelesenen Daten dann per
write nur noch abholen. Dadurch müsst man unnötigerweise zwei
Systemaufrufe mit allem Overhead durch Kontext- bzw. Adressraumwechsel
usw. machen. ioctl wäre hingegen nur ein Aufruf.
- SPI kann sowohl mit Wortbreiten ungleich acht Bit umgehen als auch
Transfers mit mehr als einem Byte/Wort durchführen. Im Gegensatz zur
üblichen Verwendung von character devices, bei denen die Anwendung davon
ausgehen kann/muss, dass Zeichenfolgen getrennt oder zusammengefasst
werden, kommt es bei SPI sehr darauf an, die exakt passende Anzahl von
Zeichen zu einem Transfer zusammenzufassen. Auch wenn man bei read/write
die Puffergrößen übergibt, bedeutet dies nur die Puffergrößen auf der
Anwendungsseite, nicht auf Geräteseite. Bei dem obigen Beispiel mit
spidev und ioctl wird jedoch die Transfergröße auf Geräteseite korrekt
definiert.
Hi,
danke für deine Infos.
Warum read und write. Das war so in dem Beispiel das ich Vorlage
verwendet hatte so vorhanden. Und ich hatte ganz zu Anfang überlegt, die
Ansteuerungen per "LEDn ON/OFF" Befehl auf das entsprechende Device zu
realisieren. Also die Bytes nicht einfach transparent durchzureichen.
Dank deiner Beschreibung sehe ich das nun genauso, zumindest beim
FullDuplex Betrieb, spart das deutlich Ressourcen. Das mit der
definierten Länge der SPI Telegramme macht auch Sinn.
Hat aus meiner Sicht nur unter Umständen den Nachteil, dass der
Konsument des Treibers selbst den SPI Datenstrom selbst definiert.
Wobei man auch dabei wahrscheinlich in der entsprechenden io_ctl
Implementierung die übergebenen Datenströme überprüfen könnte.
Nochmals vielen Dank für den Post, der hat mich dazu gebracht mal
genauer über die "Konsumentenseite" nachzudenken.
Gruß,
Sebastian