Hallo zusammen,
ich möchte zu Lehrzwecken einen Treiber unter Linux schreiben. Ich habe
dazu auch ein gutes Tutorial gefunden. Allerdings mehr oder minder für
die Grundfunktionen eines Treibers. Programmiert wird das Treibermodul
in C.
Die Idee: Meine Projekt (die meist mit atMega laufen) direkt per Treiber
anzusprechen. Zum Beispiel über die write Funktion einen Befehl an dem
µC senden. Dabei muss der Befehl entweder durch einen
USBtoSerial-Converter oder per Ethernet über IP/UDP an einen Port
gesendet werden. So will ich mir die eigene Eingabe über einen Terminal
sparen und das Ansprechen mit einem anderen Programm vereinfachen.
Dazu erstmal eine Verständnisfrage: Ein normaler Geräte Treiber spricht
Hardware direkt an, wie zum Beispiel die RS232 (Serielle Schnittstelle).
Die Treiber sind unter Linux vorhanden. Dies muss bzw. will ich nicht
programmieren. (Für was auch? :) ) Aber ist es überhaupt möglich per
Gerätetreiber ein Gerät hinter einem anderen Gerät anzusprechen? Was ich
meine: Kann ein Treiber unter Linux über einen anderen Treiber (z.B.:
USB (USBtoSerial) ein Gerät(atMega) ansprechen? Ist es überhaupt möglich
ein Gerät zu "emulieren", dass über Ethernet (ein Netzwerktreiber)
gesteuert wird?
Meine Frage, die sich auftun: Wenn es möglich ist, zum Beispiel per
USBtoSerial den µC dahin anzusprechen, kann ich dann einfach die
Serielle Schnittstelle öffnen? Also Öffnen wie in einem Programm auch?
Ich denke, dass dies nicht so einfach möglich ist, da wir uns im
Kernelmodus befinden. Gibt es dazu ein Lösung?
Vielleicht hat jemand damit schon Erfahrungen gemacht und könnte mir ein
paar Hilfestellungen oder Tipps in der Programmierung geben.
Treiber brauchst Du für HW Zugriff den das OS normalerweise unterbinden
würde weil ein Programm im Userspace die Rechte nicht hat.
Wozu brauchst Du für das Vorhaben einen Treiber?
cyberhong schrieb:> Wenn es möglich ist, zum Beispiel per USBtoSerial den µC dahin> anzusprechen, kann ich dann einfach die Serielle Schnittstelle> öffnen? Also Öffnen wie in einem Programm auch?> Ich denke, dass dies nicht so einfach möglich ist,> da wir uns im Kernelmodus befinden.
Internet per ppp ist vielleicht ein Beispiel wo das gemacht wird. An
einem klassischen COM-Port hängt ein serielles Modem. Dann muss es etwas
geben, was ppp versteht und am einen Ende mit dem Modem redet. Am
anderen Ende sieht dieses Etwas wie ein Netzwerk-Interface aus. Dieses
Etwas läuft doch bestimmt im Kernelmodus, also darf man es Treiber
nennen(?)
cyberhong schrieb:> Die Idee: Meine Projekt (die meist mit atMega laufen) direkt per Treiber> anzusprechen. Zum Beispiel über die write Funktion einen Befehl an dem> µC senden. Dabei muss der Befehl entweder durch einen> USBtoSerial-Converter oder per Ethernet über IP/UDP an einen Port> gesendet werden.
Für beides brauchst du keinerlei eigene Treiber zu schreiben, die hat
Linux bereits.
Was du wahrscheinlich eigentlich willst, ist eine Anwendung zu
schreiben, die die vorhandenen Treiber benutzt.
Also: Lerne programmieren!
Anwendungen für meine Projekte habe ich breits geschrieben. Mich
interessiert die Treiberprogrammierung.
Mit einem Treiber besteht die Möglichkeit unter Linux einfach durch open
oder write Befehle ausführen zu lassen oder vom µC zu lesen ohne das
Serielle Interface zu öffnen, Baud usw. eingeben zu müssen. Das könnte
der Treiber übernehmen. Damit könnte der Anwender oder ein anderes
Programm einfach die Dateioperationen benutzen und muss im Hintergrund
nicht wissen, wie die Hardware tatsächlich angesprochen wird.
Ween ich es richtig verstehe willst Du einen Linux-Kernel-Treiber der
eine serielle Schnittstelle benutzt (nicht bereitstellt).
Also:
User-Space <-> Treiber <-> serielle Schnittstelle (UART, USB, ...) <->
Device
Das Stichwort bei Linux heisst "serdev". Da gibt es im Kernel ein paar
Beispiele (v.a. für GPS- oder Bluetooth-Devices):
http://events17.linuxfoundation.org/sites/events/files/slides/serdev-elce-2017-2.pdfhttps://elixir.bootlin.com/linux/latest/source/include/linux/serdev.hhttps://elixir.bootlin.com/linux/latest/C/ident/serdev_device
Es ist sowieso am besten einen möglichst ähnlichen Treiber zu suchen,
den zu kopieren (neuer Name) und dann anzupassen als bei 0 anzufangen...
Was noch zu überlegen ist, ist wie das User-Space-Interface aussieht. Ob
es ein /dev werden soll, das man per ioctl() oder read/write()
ansteuert. Oder ein File in /proc oder /sys.
Und es stellt sich die Frage wie man dem Kernel nun sagt dass der neue
Treiber ein ganz bestimmten seriellen Port verwenden soll. Auf vielen
Systemen geht das per Device-Tree.
Nikolaus S. schrieb:> Ween ich es richtig verstehe willst Du einen Linux-Kernel-Treiber der> eine serielle Schnittstelle benutzt (nicht bereitstellt).>> Also:>> User-Space <-> Treiber <-> serielle Schnittstelle (UART, USB, ...) <->> Device
Genau das habe ich vor ;) Danke für deinen Beitrag
Pandur S. schrieb:> Und wie wird das Serialport configuriert ?
Das könnte man beim Laden des Treibers herausfinden. Er schickt einfach
eine Anfrage an alle Ports raus, der µC antwortet dann mit einer Magic
Number. So mache ich das Programmen auch schon. Damit muss der Anwender
nicht den Port oder Schnittstelle kennen....
Treiber für eine USB-Soundkarte sprechen auch die Karte an über USB,
dass es vor dem Einstecken der Karte schon gab. Das Einstecken der Karte
erzeugt ein neues USB-Device dass du mit deinem Treiber ansprechen
kannst.
cyberhong schrieb:> Das könnte man beim Laden des Treibers herausfinden.
Vielleicht solltest du einen anderen Ausdruck verwenden:
Du willst keinen Hardware-Treiber programmieren. Den gibt es schon im
Kernel und ud wirst das nicht besser hinbekommen.
Vielmehr willst du eine Programmbibliothek (Library) schreiben die
diverse Applikationen einbinden und die dann je nach Einstellungen oder
Aufruf die Kommunikation mit dem Hardware-Treiber herstellt.
Es gibt einen off-tree kernel for das Pinephone Pro, wo ein Treiber
drinn ist, um ein Modem ein und aus zu schalten. Das nimmt sich einen
Seriellen Anschluss (definiert per device tree), der dann im userspace
nicht mehr zur verfügung steht, setzt ein paar GPIOs und sended ein paar
AT befehle beim ein / ausschalten vom Modem.
https://megous.com/git/linux/tree/drivers/misc/modem-power.c?h=orange-pi-5.16#n501
Ich spreche alle möglichen Geräte (seriell oder USB) nur im Usermodus
an. Dazu brauche ich in Linux keine extra Treiber.
Serielle Schnittstellen (z.B. /dev/ttyS0, oder wie sie auch immer
heissen) kann man einfach mit open() öffnen, mit write() beschreiben.
USB-Serial Adapter werden vom Linux-Kernel meist schon auf seriell
gemappt, so dass der Weg mit open() auch geht.
Bei komplexeren USB-Geräten nehme ich die LibUSB. Die kann ich über eine
API im Userspace ansprechen.
Und bei Kernel-Treibern muss man für jede neue Kernelversion auch den
Treiber neu kompilieren. Will ich nicht, zu aufwändig.
Bleib im Userspace. Versuche erst einmal auf normalen Leveln eine
Kommunikation hin zubekommen.
Ich habe den TO so verstanden dass er - zu Lernzwecken - einen
Linux-Treiber schreiben möchte.
Als Beispiel hat er die Kommunikation mit einem Atmel mittels Serieller
Schnittstelle (oder USB) ins Spiel gebracht.
Jetzt ist das zwar nicht direkt ein Szenario wo man einen eigenen
Treiber schreiben müsste. Das geht, wie festgestellt wurde, auch alles
im Userspace.
Dennoch unterscheidet sich das Vorgehen beim Schreiben eines "normalen"
Userspace-Programms doch sehr von der Programmierung eines Treibers
welcher im Kontext des Kernels läuft.
Deswegen: ja, auch wenns einfacher ginge, solange der Lerneffekt im
Vordergrund steht halte ich die Idee des TOs für durchaus sinnvoll.
Die Idee hat auch den Charm dass man Teile des Programms, z.B. einen
etwaigen Kommando-Parser, Statemachines für die Kommunikation etc.
erstmal als Userspace-Programm testen und dann in den Kernelspace
portieren kann.
@ TE: ignorier die Unken, finde deine Idee gut. Du hast dir ein schönes
Beispiel ausgewählt.
cyberhong schrieb:> Die Idee: Meine Projekt (die meist mit atMega laufen) direkt per Treiber> anzusprechen.
Deine Idee ist wirr und mir nicht verständlich..
Also: Ein Treiber ist ein Stück Programm, das eine
Betriebssystem-Funktion auf eine bestimmte Peripherie der Rechenkiste
leitet. Ein außen an irgend eine Schnittstelle angeschlossener µC ist
jedoch keine Peripherie, sondern eben bloß ein drangestecktes Gerät.
Wenn dein µC über eine der üblichen Schnittstellen an deinen PC
angeschlossen ist, z.B. einen seriellen Kanal, dann können alle
Programme über diesen Kanal mit dem µC kommunizieren. Dabei ist es
völlig wurscht, ob ein Teil dieses Kanals über einen USB geht oder von
einem reitenden Marsmännchen erledigt wird. Was du vermutlich am ehesten
benötigst, ist ein Protokoll auf dem Kanal, an das sich alle Programme
halten sollen. Nun, das mußt du dir selber ausdenken, aber das ist kein
Teil irgend eines Kanals, der aus dem PC heraus bzw. herein führt.
Insofern brauchst du keinen Treiber zu schreiben.
Ich geb dir mal einige Beispiele:
1. die klassische Kommandozeile. Da wird jedes Kommando auf einer mit
Zeilenschaltung (bei Windows: CR und LF) abgeschlossenen Kommandozeile
mit Echo gesendet, für Ausgaben des µC zum PC hin gibt es keine
Festlegungen.
Beispiel:
1
DISPLAY1001FF(cr)(lf)
2. die einfache, aber noch lesbare Maschine zu Maschine Kommunikation:
dort werden Kommandos, die aus Einzelbuchstaben bestehen, mit eventuell
vorangestelltem rein numerischen Parameter OHNE Echo gesendet. Ausgaben
sind wieder nicht festgelegt.
Beispiel:
1
64A127D
3. wie 1 oder 2, aber der Zeichenvorrat ist wertemäßig aufgeteilt.
Beispiel:
0..0F - Kommandoverkehr
10..1F - Alternativ-Verkehr
20..5F - Kommandoverkehr
60..7F - Statusmeldungen
80..FF - Alternativ-Verkehr
Hierbei können beide Geräte (PC und µC) anhand des transferierten Wertes
unterscheiden, zu welchem Funktionsbereich der transferierte Wert
gehört. Damit können in beiden Richtungen Informationen zu
unterschiedlichen Funktionsgruppen bzw. Programmteilen geschickt werden,
ohne daß sie sich gegenseitig stören.
Man kann sich so oder anders verschiedenste Protokolle ausdenken, mit
denen man über einen (ziemlich beliebigen) Kanal die Kommunikation
zwischen Programmen auf dem PC und der Firmware in einem µC organisieren
kann.
Ich vermute mal, daß dir im Grunde irgend so etwas als Ziel der ganzen
Übung erscheint.
W.S.
W.S. schrieb:> Ich vermute mal, daß dir im Grunde irgend so etwas als Ziel der ganzen> Übung erscheint.
Nö, er möchte als Übung einen Linuxtreiber schreiben. Der könnte auch
über USB mit AVR sprechen. Eben z.B. ein bestimmtes Protokoll.
900ss D. schrieb:> Nö, er möchte als Übung einen Linuxtreiber schreiben. Der könnte auch> über USB mit AVR sprechen. Eben z.B. ein bestimmtes Protokoll.
Wenn einer ohne Not einen Gerätetreiber für sein OS zu schreiben
beabsichtigt, dann nur zu! Einfach machen. Das war ein Zitat.
Aber so ein Treiber interessiert sich normalerweise nicht für die
Bedeutung der Daten, die er von A nach B zu schaufeln hat. Auch nicht
für irgendwelche Protokolle. Auch nicht für die Bauart eines an den PC
gestöpselten µC. Allenfalls interessiert sich irgend eine andere
Verwaltungsstelle im OS für vid&pid, falls das Ganze sich auf den USB
beziehen sollte.
W.S.
W.S. schrieb:> Aber so ein Treiber interessiert sich normalerweise nicht für die> Bedeutung der Daten,
Sicher könnte er aber. Er könnte doch ein spezifisches Protokoll
sprechen?
Ich sehe da eher, dass jemand ein Block-Device bauen will, auf das er
mit Dateioperationen zugreifen will. Für Seriell ist das aber nicht
notwendig, da der Kernel hier bereits ein Blockdevice anbietet.
Angenommen das Gerät ist über RS232 angebunden und versteht, aus
Platzgründen, nur Einzeichenkommandos oder nur eine binäre Übertragung.
In diesem Fall könnte man schon ein Block-Device verwenden, um die
Kommandos in, für Menschen lesbare Form zu bringen. Schickt man eine CSV
an das Device, würden die Daten beispielsweise in die benötigte Form
umgewandelt oder auch komprimiert.
So könnte man beispielsweise einen µP direkt mittels "cat daten.bin >
/dev/winzigweich" programmieren.