Autor Thema: Was genau ist eigentlich ein "Hardware Abstraction Layer" in einem OS?  (Gelesen 12640 mal)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 14. February 2011, 12:45 »
Und wer fixt jetzt http://www.lowlevel.eu/wiki/HAL? ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 14. February 2011, 13:35 »
Hallo,


Und wer fixt jetzt http://www.lowlevel.eu/wiki/HAL? ;)
Eigentlich wollte ich ja auf den Fragesteller verweisen aber ich hab dann doch mal schnell ein paar Zeilen dazu gepackt. Ich hoffe das ist so richtig.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 14. February 2011, 21:52 »
Nachdem wir uns einig sind, dass ein HAL eine Abstraktion ohne Code ist, kann ich ja noch ein bisschen Senf draufkippen.

Du vergisst den MCA, dort ging das auch. Aber es gab m.W. keine MCA-auf-MCA-Brücken.
Nebst dessen das MCA zwar P&P hatte aber wimre nicht in der Lange war die Karten ihrer realen physischen Position auf dem Main-Board (Slot-Nummer o.ä.) zuzuordnen.
Die Ressourcen einer MCA-Karte sind m.W. von ihrer physischen Slot-Position abhängig.

Es sind trotzdem Busse. Und wenn du eine USB-PCI-Bridge haben könntest, dann liegt dahinter irgendwo trotzdem ein PCI-Bus.
Ich finde Du klammerst Dich zu arg an dem Wort Bus fest. ;)
Eine Frage der Sichtweise. Ein Bus ist für mein Verständnis das Teil, wo ich Daten auf einer Seite reingebe und Daten auf der anderen Seite rauskommen. Das schließt I²C, SPI und USB mit ein.

Dass du unter Bus etwas PCI-artiges verstehst, ist eine andere Sichtweise. Wenn du nur hochgezüchtete Bussysteme als Bus bezeichnen möchtest, fallen die simplen µC-Busse natürlich aus dem Rahmen.

Ich betrachte den ganzen Krempel eher aus Sicht der Software, also schaue ich durch die Abstraktion auf den physischen Teil. Und da besteht ein Bus in erster Linie aus Daten, die hindurchgehen, die ein Ziel (oder eine Quelle) haben. Dazu kommt dann noch ein Anteil durch die Abstraktion rein, der die Unterschiede zwischen den Bussen wegabstrahiert. Also Erkennung angeschlossener Geräte (Probing oder harte Konfiguration eingeschlossen), die Konfiguration der Ressourcen (auf Hardwareseite), Übermittlung von Ereignissen (HotPnP, Überspannung) und so weiter.

Am Ende ist der Bus nur ein Medium, wo du Daten reinkippen und rausholen kannst.

PCI kennst Speicher- und IO-Ressourcen und jeder Teilnehmer kann welche anbieten aber auch selbstständig auf die Angebote anderer Teilnehmer zugreifen.
USB hingegen kennt nur Kommunikations-Pipelines die auch immer mit einem Ende bei einem Host-Controller (quasi der Wurzel des USB-Baums) enden müssen und auch nur der Host kann von sich aus aktiv werden alle Devices müssen grundsätzlich auf das Polling vom Host warten.
Das sind Unterschiede, die durch eine gute Abstraktion vernichtet werden sollten.

Zwischen PCI und USB gibt es IMHO keine funktionalen Übereinstimmungen so das es wohl absoluter Blödsinn ist beide hinter einer gemeinsamen Abstraktion zu verstecken, nebst dessen das es keine Geräte mit identischer Ansteuerung für PCI und USB gibt.
Aha. Datenübertragung, der Sinn eines jeden Bussystems, ist also keine Gemeinsamkeit von PCI und USB. Das ist mir neu.

Noch nicht einmal die Vendor-IDs sind identisch vergeben so das man schon bei den elementarsten P&P-Funktionen zwischen PCI und USB unterschieden muss.
PCI mit I²C oder SPI in einen Topf zu werfen macht IMHO noch viel weniger Sinn da die Unterschiede nur noch mehr werden.
Du verstehst, glaube ich nicht, was ich mit Abstraktion meine.

Eine Abstraktion für Bussysteme ist eine Sammlung von Funktionen, also eine API, die der Treiber benutzen kann, um mit der am Bus angeschlossenen Hardware zu reden. Das heißt nicht, dass du einen großen, gemeinsamen Treiber für PCI und I²C entwickeln sollst!

Das heißt nur, dass dein PCI-, USB- und auch I²C-Treiber eine einheitliche Sammlung von Funktionen bereitstellen - die dann von den jeweiligen Hardwaretreibern genutzt wird. Jeder Treiber für eine Bus-Bridge stellt ebenfalls diese API bereit und schon funktioniert dein Temperatursensor für I²C, wenn du ihn an eine USB-I²C-Bridge anschließt, aber der gleiche Treiber kann auch die Temperatur vom Mainboard lesen oder vom DDC-Teil des VGA-Anschlusses.

Für jedes Bussystem hast du trotzdem einen eigenen Treiber. Alles andere ist Blödsinn.

Wenn man wirklich PCI über USB tunneln möchte (was sicher irgendwie funktionieren könnte wenn auch nicht sonderlich gut) dann wäre dieser Tunnel für die PCI-Hardware und die Treiber völlig transparent (also aus PCI-Sicht unsichtbar), anders lassen sich Dinge wie Busmasterring oder klassische Interrupts der Geräte hinter dem Tunnel nicht realisieren.
Ich rede nicht von einem Tunnel. Ich rede von einer Bridge. Dass diese für USB->PCI extrem aufwendig ist, weil die Bussysteme physisch gesehen große Unterschiede aufweisen, steht nicht zur Debatte.

Eine recht simple Schaltung baut dir einen ISA-Bus (8-Bit) an den Parallelport. Das geht recht einfach, wenn du auf Interrupts verzichten kannst. Es wird geringfügig schwieriger, wenn du den ISA-Bus komplett nachbilden möchtest. Du kannst auch I²C-Busse problemlos an einen Parallelport, an einen USB-Anschluss oder an einen PCI-Bus hängen, all das gibt es. Und PCI-PCI-Bridges ist auch kein Thema, siehe CardBus (obwohl die physische Schicht dort trotzdem anders aussieht).

Klar benötigt dieser Tunnel eine Konfiguration aber ein HAL ist das IMHO absolut nicht (weil ja für die PCI-Geräte und PCI-Treiber alles so bleibt wie es die PCI-Spec festlegt).
Eine Abstraktion ist eine Softwareschnittstelle, keine Hardwareschnittstelle. Dem Temperatursensor ist total egal, wie der I²C-Bus mit dem Rechner verbunden ist, er sieht nur I²C. Und PCI-Hardware sieht nur einen PCI-Bus. Logisch, sonst funktioniert die Hardware ja nicht.

Der Unterschied (mit/ohne HAL) liegt darin, ob dein Hardwaretreiber weiß, wo das Gerät angeschlossen ist, oder nicht. Und ob es prinzipiell einen Unterschied macht, ob da eine Bridge zwischen ist, oder nicht. Das ist alles.

Im Falle von USB ist es meinem Microcontroller nicht egal, ob ich einen Hub angeschlossen habe. Der kann nämlich mit Hubs nichts anfangen (und auch sonst nur exakt ein einziges Gerät verwalten, dessen Treiber ich zudem bei der Initialisierung des USB-Stacks mit angeben muss).

Und wenn du nicht "Bussystem" abstrahieren möchtest, weil dir das widerstrebt, dann ist eine Abstraktion für "USB-Bus" trotzdem ein Teil des HAL (oder ein Teil deines USB-Stacks, oder des EHCI-Treibers, Implementierungsdetail). Im Endeffekt sollte es dem Gerätetreiber egal sein, ob ein USB-Hub unterwegs auftaucht oder nicht, ob du den Anschluss gewechselt hast oder nicht oder ob das Hardwareprotokoll nun USB 1.1 oder USB 2.0 kann.

Die Summe aller solcher APIs, die jeweils ein Stückchen Hardware abstrahieren, ist der HAL.
Das trifft aber auch auf Treiber zu oder auf das OS als ganzes (aus Sicht der Applikationen).
Der HAL abstrahiert Hardware für Treiber, im Gegensatz zu den Treibern, die für das Betriebssystem abstrahieren. Und das Betriebssystem als Ganzen abstrahiert für Anwendungen.

Leg die Dinge nicht immer so aus, wie ich sie gerade nicht gemeint habe. ;-)

Der typische Benutzer sieht ein Fenster mit Word drin und der Computer ist "der graue Kasten unterm Tisch". Es geht ihm nicht darum, die Maus zu bewegen, sondern er "klickt auf den Druckerbutton und dann kommt bedrucktes Papier aus dem Drucker".
Sorry, aber das kannst Du Deiner Oma so erklären, hier sind wir auf lowlevel.eu und da ist diese Art von Erklärung IMHO weniger geeignet.  ;)
Es geht um die Sichtweise, und für gewisse Anwendungsfälle ist eine gewisse Sichtweise vorteilhaft, eine andere wiederum untauglich.

Ich glaube, wenn ich "Festplatte" sage, denkst du in erster Linie auch ein Kästchen mit SATA-/IDE-/USB-Anschluss und bist der Meinung, dass da Daten in Form von Sektoren draufgepresst werden. Das ist aus Sichtweise des Betriebssystems eine hinreichend gute Abstraktion. Da fallen übrigens auch SSDs (und im weiteren Sinne USB-Sticks oder CF-Karten) drunter.

Für Anwendungsprogramme ist eine Festplatte etwas, wo ein Pfad dransteht und Dateien drauf sind.

Für Datenrettungsunternehmen und Festplattenhersteller ist eine Festplatte etwas, was aus rotierenden, beschichteten Metallscheiben besteht, die entsprechend physischer Gesetze magnetisiert werden und den Magnetisierungszustand mit halbleitertechnologischen Mitteln rein- & rausschieben können. Für einen OS-Entwickler ist diese Sichtweise natürlich blödsinnig.

Dein Ziel ist es aber, die Anwendung zu bedienen.
Absolut richtig, trotzdem kann ich als OS-Dever nicht den langen und komplizierten Weg der Informationen vom User bis zur eigentlichen Applikation und zurück ignorieren.
Du kannst aus Sicht der Anwendungen schauen, du kannst es aber auch aus Sicht der Hardware tun. Im einen Fall ist die Hardware das Ende der Kette, im anderen Fall in der Mitte.

Man nimmt sich immer nur die Teilaspekte raus, die man gebrauchen kann, das solltest du wissen. Und für meine Betrachtung eines HAL ist mein Bild nunmal recht gut geeignet. Wenn du von einer ganz anderen Sicht auf die Dinge ausgehst, ist es ungleich schwieriger, da noch was zu sehen.

Eben genau deswegen ist es offensichtlich so enorm schwierig den HAL als Einzel-Komponente exakt zu definieren.
Der HAL ist keine Einzelkomponente, sondern er definiert das Zusammenspiel verschiedener Komponenten.

Gruß,
Svenska

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 15. February 2011, 15:50 »
Hallo,


Die Ressourcen einer MCA-Karte sind m.W. von ihrer physischen Slot-Position abhängig.
Echt, hm, wusste ich gar nicht.
Für einen kurzen Moment war ich versucht mir die MCA-Spec im iNetz zu suchen aber da bleibe ich doch lieber bei meinem Totschlagargument das der MCA bereits vor über 20 Jahren ausgestorben ist und daher nicht zählt. :P

Es sind trotzdem Busse. Und wenn du eine USB-PCI-Bridge haben könntest, dann liegt dahinter irgendwo trotzdem ein PCI-Bus.
Ich finde Du klammerst Dich zu arg an dem Wort Bus fest. ;)
Eine Frage der Sichtweise. ....
Natürlich ist es eine Frage der Sichtweise. Ich glaube ich verstehe jetzt warum wir da so gänzlich aneinander vorbei schreiben, deswegen möchte ich das noch mal etwas anders erklären und hoffe das Du mich dann auch besser verstehst.

Du gehst zu erst von den Gemeinsamkeiten aus, da ist im wesentlichen das Durchleiten von Daten, und versuchst dann die Unterschiede, das sind ne ganze Menge, hinter einer Abstraktion zu verstecken.
Ich hingegen schaue mir die verschiedenen Busse im Detail an und versuche dann anhand der jeweiligen Eigenschaften/Fähigkeiten zu kategorisieren und da fallen PCI, USB und I²C alle in unterschiedliche Kategorien. ISA ist zu PCI sehr ähnlich (EISA und MCA auch), von den unterstützten Konzepten wie IO, MMIO, Interrupts usw., und würde deswegen von mir mit in den PCI-Topf reingeworfen werden. Obwohl bei ISA ein paar Sonderdinge wie DMA-Kanäle zu beachten sind aber da diese in der realen Praxis keine echte Rolle mehr spielen (weil ja der Floppy-Controller in heutigen PCs der einzigste und letzte Nutzer der ISA-DMA-Kanäle ist) kann ich sie recht gut ignorieren. Was anderes wäre es wenn ich viele vollwertige ISA-Komponenten managen müsste und ich die Besonderheiten von ISA auch wirklich anständig verwalten müsste, dann würde ich wohl ISA nicht in den PCI-Topf werfen sondern lieber eine extra ISA-Kategorie aufmachen. Mir ist klar das meine Vorgehensweise aus akademischer Sicht nicht ganz korrekt ist aber ich persönlich empfinde sie als praktisch und zweckdienlich.
Wenn ich versuchen würde PCI, USB und I²C auf eine gemeinsame Basis zu stellen dann wäre die extrem klein (weil es eben nur sehr wenige Gemeinsamkeiten gibt, eigentlich nur eine nämlich das Durchleiten von Daten) so das ich für jede Unterkategorie einen recht großen Funktionsumfang bieten muss (für PCI benötige ich MMIO, Busmastering, Interrupts; für USB benötige ich Device-IDs, Endpunkte und Pipes; für I²C benötige ich die 7-Bit-Adressen und Baudraten). Es würde aus meiner Sicht schlicht nichts bleiben was wirklich in eine gemeinsame Basis-API kommen könnte, nebst dessen das es für diese Basis-API einfach keine Verwendung gibt weil es eben keine HW-Komponenten gibt die sich auch nur theoretisch mit einem Treiber über verschiedene Busse ansprechen lassen (von NE2000 mal abgesehen aber ISA und PCI sind sich eben doch noch recht ähnlich was z.B. auf ISA, USB und SPI nicht mehr zutrifft). Als HW-Entwickler nehme ich die Anforderungen die das Gerät erfüllen muss und suche mir dann das geeignete Bus-Interface aus (die Wahl wird natürlich für einen Temperatur-Sensor anders ausfallen als für ein 10GB-Ethernet-Interface), danach entwickle ich diese HW so das sie aus den spezifischen Fähigkeiten und Eigenheiten des gewählten Busses das Maximum raus holt. Ich persönlich finde das sich eine gemeinsame Basis nur dann lohnt wenn sie auch groß genug ist und es auch genügend Anwender dafür gibt und genau das trifft auf die Abstraktion von unterschiedlichen Bussen eben nicht zu.

Es ist nicht meine Absicht Deine Sichtweise in irgendeiner Form zu bewerten (schon gar nicht als falsch denn das ist sie nicht), ich bin nur der Meinung das Deine Sichtweise für OS-Dev nicht so ganz passend ist.

Alle von uns genannten Bussysteme sind wichtig und nützlich und keiner ist besser oder schlechter als ein anderer aber sie sind eben für komplett unterschiedliche Anwendungen konzipiert und haben daher auch gänzlich unterschiedliche Fähigkeiten und Eigenheiten. Wenn Du diese Unterschiede wegabstrahierst dann tust Du in meinen Augen den Bussystemen unrecht, lass ihnen doch ihre Individualität und erfreue Dich an der Vielfalt. ;)

Wenn du nur hochgezüchtete Bussysteme als Bus bezeichnen möchtest, fallen die simplen µC-Busse natürlich aus dem Rahmen.
Da hast Du mich wohl missverstanden. Das ich so auf PCI aus bin liegt einfach daran das er für die CPU und der darauf laufenden Software direkt erreichbar ist. In C kann man den MMIO-Speicherbereich einer PCI-Karte mit einem simplen Pointer (ist elementarer Teil der Programmiersprache) ansprechen, für die Ressourcen einer USB/I²C/SPI-Hardware gilt das erstmal nicht (da wird immer eine Art Library benötigt). PCI ist in heutigen Computern nunmal der Haupt-Bus und alles andere hängt dahinter (per Host-Controller o.ä. zugänglich).

PCI kennt Speicher- und IO-Ressourcen und jeder Teilnehmer kann welche anbieten aber auch selbstständig auf die Angebote anderer Teilnehmer zugreifen.
USB hingegen kennt nur Kommunikations-Pipelines die auch immer mit einem Ende bei einem Host-Controller (quasi der Wurzel des USB-Baums) enden müssen und auch nur der Host kann von sich aus aktiv werden alle Devices müssen grundsätzlich auf das Polling vom Host warten.
Das sind Unterschiede, die durch eine gute Abstraktion vernichtet werden sollten.
Wie?

Aha. Datenübertragung, der Sinn eines jeden Bussystems, ist also keine Gemeinsamkeit von PCI und USB. Das ist mir neu.
Natürlich ist das eine Gemeinsamkeit (noch nicht mal die einzigste) von PCI und USB aber wie willst Du das hinter einer gemeinsamen API verstecken? Einer PCI-Karte schicke ich Daten indem ich über einen Pointer auf Speicher zugreife (oder die Karte sich die Daten gleich selber aus dem Haupt-Speicher holt) und für USB muss man eine Funktion/Syscall/o.ä. aufrufen, da gibt es aus Sicht der Software keine gemeinsame Basis.

Das heißt nur, dass dein PCI-, USB- und auch I²C-Treiber eine einheitliche Sammlung von Funktionen bereitstellen - die dann von den jeweiligen Hardwaretreibern genutzt wird.
Was soll in diese einheitliche API denn rein?

Jeder Treiber für eine Bus-Bridge stellt ebenfalls diese API bereit und schon funktioniert dein Temperatursensor für I²C, wenn du ihn an eine USB-I²C-Bridge anschließt, aber der gleiche Treiber kann auch die Temperatur vom Mainboard lesen oder vom DDC-Teil des VGA-Anschlusses.
So in der Art hab ich mir das vorgestellt. Es gibt einen I²C-Master-Manager an den von unten die verschiedenen Host-Controller-Treiber andocken (egal ob der I²C nun von einem USB-basierten Host-Controller, einem PCI-basierten Host-Controller, irgendwelchen SMB-Registern im Chipsatz oder von der GPU kommt) und von oben die eigentlichen Geräte-Treiber die ein einfaches/einheitliches Interface bekommen so das sie mit jeder I²C-Implementierung umgehen können. Vergleichbare Instanzen gibt es für USB, SPI und auch für PCI (jeweils mit einem Funktionsumfang der genau zu dem entsprechenden Bus passt) aber diese APIs haben (fast) keine Gemeinsamkeiten so das es IMHO nichts bringt sie auf einer gemeinsamen Basis aufzusetzen (nebst dessen das es für diese Basis nach wie vor niemanden gibt der sich dafür interessieren würde).

Für jedes Bussystem hast du trotzdem einen eigenen Treiber. Alles andere ist Blödsinn.
Also doch keine gemeinsame API? Ja was denn nun?

Wenn man wirklich PCI über USB tunneln möchte (was sicher irgendwie funktionieren könnte wenn auch nicht sonderlich gut) dann wäre dieser Tunnel für die PCI-Hardware und die Treiber völlig transparent (also aus PCI-Sicht unsichtbar), anders lassen sich Dinge wie Busmasterring oder klassische Interrupts der Geräte hinter dem Tunnel nicht realisieren.
Ich rede nicht von einem Tunnel. Ich rede von einer Bridge. Dass diese für USB->PCI extrem aufwendig ist, weil die Bussysteme physisch gesehen große Unterschiede aufweisen, steht nicht zur Debatte.
Ich glaube nicht das eine echte Bridge zwischen USB und PCI wirklich realisierbar ist. Anderes Beispiel: wenn man normale Straßenfahrzeuge (aka Autos) mit der Bahn transportiert dann werden die nicht in Schienenfahrzeuge umgewandelt sondern in einem Waggon versteckt (für die Bahn-Gleise ist nur dieser Waggon sichtbar), dieses Konzept entspricht dem Tunneln. Der Grund warum man das so macht ist der das die konzeptionellen Unterschiede einfach zu groß sind (auch wenn beide auf runden Rädern basieren und noch viele andere Gemeinsamkeiten haben) und das selbe trifft auch auf USB und PCI zu. Unüberbrückbare Unterschiede können auch nicht von noch so vielen Gemeinsamkeiten überwunden werden.

Eine recht simple Schaltung baut dir einen ISA-Bus (8-Bit) an den Parallelport. Das geht recht einfach, wenn du auf Interrupts verzichten kannst.
Da müsste man noch auf sehr viel mehr verzichten als nur Interrupts, z.B. auf DMA. Nebst dessen das ISA-Karten einen Takt erwarten der mit konstanter Frequenz arbeiten muss (ohne Unterbrechungen u.ä.) und sich die Datentransfers an diesen Takt halten müssen. So simpel dürfte diese Schaltung mit Sicherheit nicht werden (ich würde sagen einen kleinen FPGA kann man damit schon füllen und mit einem mittleren FPGA sollte man sogar ISA komplett unterstützen können).

Du kannst auch I²C-Busse problemlos an einen Parallelport, an einen USB-Anschluss oder an einen PCI-Bus hängen, all das gibt es.
Hab ich alles schon selber benutzt. I²C ist auch was anderes als ISA, u.a. ist das Timing recht flexibel bei I²C.

Der Unterschied (mit/ohne HAL) liegt darin, ob dein Hardwaretreiber weiß, wo das Gerät angeschlossen ist, oder nicht. Und ob es prinzipiell einen Unterschied macht, ob da eine Bridge zwischen ist, oder nicht. Das ist alles.
Aha, das musst Du bitte etwas ausführlicher erklären.

Und wenn du nicht "Bussystem" abstrahieren möchtest, weil dir das widerstrebt
Es widerstrebt mir nicht sondern es erscheint mir nicht sinnvoll für den OS-Dever.

dann ist eine Abstraktion für "USB-Bus" trotzdem ein Teil des HAL (oder ein Teil deines USB-Stacks, oder des EHCI-Treibers, Implementierungsdetail).
Womit der Begriff HAL wieder zu einer recht frei definierbaren Angelegenheit wird.

Im Endeffekt sollte es dem Gerätetreiber egal sein, ob ein USB-Hub unterwegs auftaucht oder nicht, ob du den Anschluss gewechselt hast oder nicht oder ob das Hardwareprotokoll nun USB 1.1 oder USB 2.0 kann.
Ja, genau so stelle ich mir das für USB vor. Und auch für I²C oder SPI sollte es genau so sein.

Der HAL abstrahiert Hardware für Treiber
Hm, wie soll das gehen und vor allem wie weit soll das gehen?

Leg die Dinge nicht immer so aus, wie ich sie gerade nicht gemeint habe. ;)
Dann schreibe bitte auch so wie Du es gerade meinst. ;)

Ich glaube, wenn ich "Festplatte" sage, denkst du in erster Linie auch ein Kästchen ....
Nö, ganz so simpel mach ich mir das nun auch wieder nicht, mir sind die verschiedenen Abstraktionsebenen durchaus bewusst.

Dein Ziel ist es aber, die Anwendung zu bedienen.
Absolut richtig, trotzdem kann ich als OS-Dever nicht den langen und komplizierten Weg der Informationen vom User bis zur eigentlichen Applikation und zurück ignorieren.
Du kannst aus Sicht der Anwendungen schauen, du kannst es aber auch aus Sicht der Hardware tun.
Beide Sichtweisen kommen bei mir zum selben Ergebnis. Die Applikation benötigt zum Fenster malen eine GUI-Library und diese setzt wieder auf einem Fenstermanager u.ä. auf welcher wiederum auf Treiber zugreift die dann schlussendlich die Hardware ansteuern mit der der User interagiert (in dem Fall der Monitor dessen Bild durch die menschlichen Augen wahrgenommen wird), auch die HW sieht nur die Treiber aber nicht die Applikation(en). Ich kann das drehen und wenden wie ich will, so wie ich das sehe sind User und Applikationen immer die zwei entferntesten Enden der Kette. Das einzigste was mir jetzt beim nochmaligen nachdenken aufgefallen ist ist das es auch HW gibt mit der der User nicht interagiert (z.B. eine Festplatte oder die Netzwerkkarte).


Der HAL ist keine Einzelkomponente, sondern er definiert das Zusammenspiel verschiedener Komponenten.
Der Satz gefällt mir wirklich, ist bis jetzt die beste Antwort auf meine ursprüngliche Frage. Danke!


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #24 am: 15. February 2011, 17:13 »
Hallo,

Mir ist klar das meine Vorgehensweise aus akademischer Sicht nicht ganz korrekt ist aber ich persönlich empfinde sie als praktisch und zweckdienlich.
Ich glaube, das trifft es recht gut.

Wenn ich versuchen würde PCI, USB und I²C auf eine gemeinsame Basis zu stellen dann wäre die extrem klein (weil es eben nur sehr wenige Gemeinsamkeiten gibt, eigentlich nur eine nämlich das Durchleiten von Daten) so das ich für jede Unterkategorie einen recht großen Funktionsumfang bieten muss (für PCI benötige ich MMIO, Busmastering, Interrupts; für USB benötige ich Device-IDs, Endpunkte und Pipes; für I²C benötige ich die 7-Bit-Adressen und Baudraten).
Der Sinn einer guten Abstraktion ist es doch, auch noch in Unterschieden die Gemeinsamkeiten zu finden. Die Baudrate eines I²C kannst du mit der Geschwindigkeit des USB-Gerätes (oder -Busses) gleichsetzen, die 7-Bit-Adresse des I²C entspricht der Vendor-/Device-ID (evtl. auch dem Endpoint) von PCI/USB. Und so weiter.

Ich finde das gut, du findest das nicht gut. Aber das wussten wir beide schon vorher. ;-)

Wenn Du diese Unterschiede wegabstrahierst dann tust Du in meinen Augen den Bussystemen unrecht, lass ihnen doch ihre Individualität und erfreue Dich an der Vielfalt. ;)
Es gibt für beide Varianten recht weit verbreitete Beispiele (NetBSD mit, Linux und Windows ohne Busabstraktion).

Das heißt nur, dass dein PCI-, USB- und auch I²C-Treiber eine einheitliche Sammlung von Funktionen bereitstellen - die dann von den jeweiligen Hardwaretreibern genutzt wird.
Was soll in diese einheitliche API denn rein?
Hatte ich schonmal beschrieben, siehe weiter oben.

Für jedes Bussystem hast du trotzdem einen eigenen Treiber. Alles andere ist Blödsinn.
Also doch keine gemeinsame API? Ja was denn nun?
Mein Gedanke: Es gibt Treiber für Bussysteme. Alle diese Treiber implementieren eine gemeinsame API, die die Unterschiede zwischen den Bussystemen wegabstrahiert und somit jedem Hardwaretreiber einen "Bus" zur Verfügung stellt. Der Treiber selbst weiß das garnicht (oder nur peripher).

Du sollst nicht einen einzelnen Treiber bauen, der gleichzeitig PCI- und USB-Hostcontroller unterstützt.

Ich glaube nicht das eine echte Bridge zwischen USB und PCI wirklich realisierbar ist. Anderes Beispiel: wenn man normale Straßenfahrzeuge (aka Autos) mit der Bahn transportiert dann werden die nicht in Schienenfahrzeuge umgewandelt sondern in einem Waggon versteckt (für die Bahn-Gleise ist nur dieser Waggon sichtbar), dieses Konzept entspricht dem Tunneln.
Genau. Und die Fahrzeuge, die sowohl Straßen als auch Schienen befahren können, die sind in dem Zusammenhang Bridges. (Solche Teile gibt es im Baugewerbe.)

Unüberbrückbare Unterschiede können auch nicht von noch so vielen Gemeinsamkeiten überwunden werden.
Keine Unterschiede sind Unüberbrückbar. Im schlimmsten Fall hast du einen Rechner mit PCI-Schnittstelle, einen weiteren Rechner mit USB-Schnittstelle und baust einen TCP/IP-Tunnel dazwischen. Ich sage ja nicht, dass eine USB-PCI-Bridge ohne weiteres herstellbar ist (der umgekehrte Fall ist wesentlich einfacher und heißt *HCI).

Eine recht simple Schaltung baut dir einen ISA-Bus (8-Bit) an den Parallelport...
Da müsste man noch auf sehr viel mehr verzichten als nur Interrupts, z.B. auf DMA. Nebst dessen das ISA-Karten einen Takt erwarten der mit konstanter Frequenz arbeiten muss (ohne Unterbrechungen u.ä.) und sich die Datentransfers an diesen Takt halten müssen.
So die Theorie.

So simpel dürfte diese Schaltung mit Sicherheit nicht werden (ich würde sagen einen kleinen FPGA kann man damit schon füllen und mit einem mittleren FPGA sollte man sogar ISA komplett unterstützen können).
Ich verweise auf diesen Link. Viel Spaß beim Basteln. ;-)

Der Unterschied (mit/ohne HAL) liegt darin, ob dein Hardwaretreiber weiß, wo das Gerät angeschlossen ist, oder nicht. Und ob es prinzipiell einen Unterschied macht, ob da eine Bridge zwischen ist, oder nicht. Das ist alles.
Aha, das musst Du bitte etwas ausführlicher erklären.
Entweder, der NE2000-Treiber weiß, dass er es jetzt mit einer ISA/PCI/USB/PCMCIA-Karte zu tun hat (weil er mit dem entsprechenden Bustreiber quatschen muss) oder er weiß es nicht, weil alle Busse für ihn gleich aussehen und er vom Betriebssystem z.B. ein "struct bus_info" bekommt, in dem eine abstrakte Adresse drinsteht.

Und reg dich über die NE2000 nicht auf, die ist ein einfach zu schönes Beispiel. ;-)

dann ist eine Abstraktion für "USB-Bus" trotzdem ein Teil des HAL (oder ein Teil deines USB-Stacks, oder des EHCI-Treibers, Implementierungsdetail).
Womit der Begriff HAL wieder zu einer recht frei definierbaren Angelegenheit wird.
Ist es ja auch.

Der HAL abstrahiert Hardware für Treiber
Hm, wie soll das gehen und vor allem wie weit soll das gehen?
Das definierst du als OS-Entwickler.

Gruß,
Svenska

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 15. February 2011, 21:08 »
Hallo,


Mir ist klar das meine Vorgehensweise aus akademischer Sicht nicht ganz korrekt ist aber ich persönlich empfinde sie als praktisch und zweckdienlich.
Ich glaube, das trifft es recht gut.
Dann sind wir uns ja einig. Ein OS muss eben vor allem in der Praxis funktionieren und nicht nur in der akademischen Theorie. ;)

Der Sinn einer guten Abstraktion ist es doch, auch noch in Unterschieden die Gemeinsamkeiten zu finden. Die Baudrate eines I²C kannst du mit der Geschwindigkeit des USB-Gerätes (oder -Busses) gleichsetzen, die 7-Bit-Adresse des I²C entspricht der Vendor-/Device-ID (evtl. auch dem Endpoint) von PCI/USB. Und so weiter.
Grundsätzlich hast Du ja Recht aber die von Dir gewählten Beispiele sind noch die einfachsten.
Wie möchtest Du das Übermitteln von Daten (bekannter weise der eigentliche Zweck eines jeden Bussystems) hinter einer einheitlichen API verstecken wenn die PCI-Karte die Daten selber per Busmastering aus dem RAM holen möchte, die ISA-Karte die über MMIO (also über ne Art memcpy) bekommen will und die USB/I²C/SPI-Geräte die Daten eben manuell verpackt übergeben brauchen?
Des weiteren stört mich an Deinen Beispielen das Du z.B. Vendor-/Device-ID und I²C-7-Bit-Adresse in einen Topf werfen möchtest, da geht doch beim programmieren die Typ-Sicherheit verloren. In was für einen Datentyp würdest Du das denn reinpacken wollen? Schon aus der Sicht eines Programmierers finde ich diese Gleichmacherei gar nicht gut.

Ich finde das gut, du findest das nicht gut. Aber das wussten wir beide schon vorher. ;-)
Die Welt wäre ja auch fürchterlich öde wenn wir alle der selben Meinung wären! Siehst Du doch genauso oder?

Mein Gedanke: Es gibt Treiber für Bussysteme. Alle diese Treiber implementieren eine gemeinsame API, die die Unterschiede zwischen den Bussystemen wegabstrahiert und somit jedem Hardwaretreiber einen "Bus" zur Verfügung stellt. Der Treiber selbst weiß das garnicht (oder nur peripher).
Hm, bis auf die gemeinsame API für alle Busse klingt das eigentlich nicht verkehrt.

Du sollst nicht einen einzelnen Treiber bauen, der gleichzeitig PCI- und USB-Hostcontroller unterstützt.
Das geht auch gar nicht in einem Bus-Treiber. Während bei PCI die Geräte eine für die CPU direkt erreichbare Ressource zugewiesen bekommen (und noch ein wenig Routing konfiguriert werden muss) und dann die Geräte-Treiber direkt mit ihrem Gerät kommunizieren können müssen bei USB alle Datentransfers durch den Treiber-Stapel durchgeschleust werden. Ich sehe einfach nicht wie Du das hinter einer einheitlichen API verstecken möchtest.

Und die Fahrzeuge, die sowohl Straßen als auch Schienen befahren können, die sind in dem Zusammenhang Bridges. (Solche Teile gibt es im Baugewerbe.)
Wenn Du das mit einer Bridge (wie z.B. einer PCI-2-ISA-Bridge) vergleichst dann haben wir ein Kommunikationsproblem. Dieses Baufahrzeug ist ein Hybrid weil es die Fähigkeiten beider Systeme vereint, einer Bridge würde entsprechen wenn man ein beliebiges Fahrzeug der einen Kategorie in ein Fahrzeug der anderen Kategorie umwandeln könnte (so wie eine PCI-2-ISA-Bridge einen PCI-Transfer entgegen nimmt und einen ISA-Transfer weiter leitet ohne den Inhalt zu ändern).

Im schlimmsten Fall hast du einen Rechner mit PCI-Schnittstelle, einen weiteren Rechner mit USB-Schnittstelle und baust einen TCP/IP-Tunnel dazwischen.
Das entspricht aber auch nicht dem was man unter einer Bus-Bridge versteht.

Ich sage ja nicht, dass eine USB-PCI-Bridge ohne weiteres herstellbar ist (der umgekehrte Fall ist wesentlich einfacher und heißt *HCI).
Ich sage schon das eine USB-PCI-Bridge nicht so ohne weiteres herstellbar ist. Und die andere Richtung (*HCI) ist auch keine Bridge sondern eine Komponente die von der CPU als reines PCI-Gerät angesprochen wird und die mit Hilfe der so empfangenen Daten den USB betreibt. Die USB-Daten werden quasi von der HW in PCI-Transfers verpackt (versteckt, so wie bei dem Waggon für Autos) und von dem USB-Treiber wieder entpackt.

Ich verweise auf diesen Link. Viel Spaß beim Basteln. ;-)
Okay, es scheint tatsächlich ISA-Karten zu geben die kein Takt-Signal benötigen (B20 ist in beiden Versionen unbeschaltet), aber die anderen Einschränken sind auch so extrem das nur sehr wenige ISA-Karten wirklich damit laufen könnten. Die als lauffähig genannten Karten basieren alle auf simplen IO-Registern um eben ein paar Ausgänge anzusteuern bzw. ein paar Eingänge abzufragen, ich würde wetten die Schaltung wäre nicht aufwendiger wenn man die Funktionalität der Karten gleich direkt nachbaut. Trotzdem bleibe ich bei der Meinung das da kein echter ISA-Bus raus kommt (irgendeiner ISA-Spezifikation entspricht das auf jeden Fall nicht).

Entweder, der NE2000-Treiber weiß, dass er es jetzt mit einer ISA/PCI/USB/PCMCIA-Karte zu tun hat (weil er mit dem entsprechenden Bustreiber quatschen muss) oder er weiß es nicht, weil alle Busse für ihn gleich aussehen und er vom Betriebssystem z.B. ein "struct bus_info" bekommt, in dem eine abstrakte Adresse drinsteht.
In Deiner Bus-Liste ist der USB zu viel, es gab mit Sicherheit keine NE2000-USB-Karte, NE2000 basiert auf IO-Registern die es bei USB eben nicht gibt. Ich kann mir problemlos eine NE2000 als HyperTransport-/EISA-/MCA- oder PCI-Express-Gerät vorstellen (weil alle diese Busse IO-Ports und Interrupts unterstützen und damit auch alle existierenden NE2000-Treiber zurecht kommen inklusive solche aus DOS-Zeiten, für diese gab es extra ein Hilfstool das die IO-Basis-Adresse und die Interrupt-Nummer aus dem PCI-Config-Space geholt hat und dann den DOS-Treiber mit den richtigen Parametern gestartet hat) aber ganz sicher nicht für USB, I²C, SPI, OneWire, SATA oder FireWire (es fehlen einfach die IO-Ports und/oder die Interrupts und direkt für die CPU bzw. für die darauf laufende Software sind diese Busse auch nicht erreichbar).

Und reg dich über die NE2000 nicht auf, die ist ein einfach zu schönes Beispiel. ;-)
Dann nenne doch Bitte einfach mal ein anderes Beispiel.


Grüße
Erik
« Letzte Änderung: 16. February 2011, 10:28 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #26 am: 17. February 2011, 21:08 »
Hallo,

Wie möchtest Du das Übermitteln von Daten (bekannter weise der eigentliche Zweck eines jeden Bussystems) hinter einer einheitlichen API verstecken wenn die PCI-Karte die Daten selber per Busmastering aus dem RAM holen möchte, die ISA-Karte die über MMIO (also über ne Art memcpy) bekommen will und die USB/I²C/SPI-Geräte die Daten eben manuell verpackt übergeben brauchen?
Der ultimative Fall ist der des Busmasterings.

Du übergibst der API also einen Zeiger auf ein Stück physischen Speicher, den die PCI-Karte sich per Busmastering holt. Der PCI-Bus-Treiber hätte also nichts zu tun. Ein ISA-Bustreiber kopiert die Daten dann mittels MMIO in die Karte. Ein USB-Bustreiber verpackt die Daten und schickt sie weg. Ein SPI-Bitbanging-Bustreiber fängt an, die Daten rauszutakten.

Eine gute API (die ich nicht entwickelt habe) orientiert sich am besten (PCI), bezieht aber die Eigenheiten der anderen Dinge mit ein. Und die konkrete Umsetzung obliegt doch dem Treiber.

Des weiteren stört mich an Deinen Beispielen das Du z.B. Vendor-/Device-ID und I²C-7-Bit-Adresse in einen Topf werfen möchtest, da geht doch beim programmieren die Typ-Sicherheit verloren.
Was hat denn das eine mit dem anderen zu tun? Typsicherheit ist ein reines Implementationsdetail, noch dazu stark abhängig von der Programmiersprache. Außerdem: Typsicherheit wofür? Deine API definiert die Datentypen, die dort verwendet werden. Die müssen nicht identisch zu dem sein, was am Ende für die Hardware da ist!

In was für einen Datentyp würdest Du das denn reinpacken wollen? Schon aus der Sicht eines Programmierers finde ich diese Gleichmacherei gar nicht gut.
Wie wäre es mit einer abstrakten 64-Bit langen 'Adresse' des Gerätes 'an einem Bus'? Bei I²C kann diese nur Werte von 0..127 annehmen, bei USB-/PCI-Geräten hast du eine 32-Bit-Adresse, die der PCI- oder USB-Bustreiber entsprechend zerlegt.

Ich sehe einfach nicht wie Du das hinter einer einheitlichen API verstecken möchtest.
*seufz* Ich lass es. Bringt nichts.

In Deiner Bus-Liste ist der USB zu viel, es gab mit Sicherheit keine NE2000-USB-Karte, NE2000 basiert auf IO-Registern die es bei USB eben nicht gibt.
Und? Dann definiert der USB-Controller, der die NE2000 ansteuert, halt zwei Endpoints. Eins für I/O-Register, eins für Speicher. Die Interrupts kannst du direkt nicht nachbilden, aber USB-Interrupt-Transfers haben eine Maximallatenz, womit du sowas nachbilden kannst.

Du sollst doch nicht den NE2000-Baustein direkt an einen USB-Bus hängen. Du gibst ihm eine Umgebung, in der er lebt (Datenein-&ausgabe) und diese Umgebung hängst du an den USB.

Gruß,
Svenska

DeepDancer

  • Beiträge: 58
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 17. February 2011, 23:42 »
Moinsen zusammen auch

Also wenn man das ganze mal ein wenig "offener" betrachtet, dann ist eigentlich das OS selber auch nur ein HAL - es abstrahiert die komplette Hardware und stellt einheitliche Funktionen zur verfügung welche JEDE Software die so geschrieben wird ansprechen kann, ohne sich Gedanken machen zu müssen was denn hinter diesen Funktionsaufrufen steckt.  :-)

Auch das angesprochene CDI ist in seiner Gesamtheit ein HAL - nur eben nicht direkt unter dem Kernel wie der klassische HAL, den man noch aus WIN-NT oder 2000 oder auch XP kennt.

Wobei es auch in Windows schon so war, dass zum Beispiel DirectX direkt auf den HAL ging und nicht mehr den Umweg über den Kernel nehmen musst und somit eben dann schneller war/ist.


Grüße,
DeepDancer

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #28 am: 18. February 2011, 11:12 »
Hallo,


Svenska, zuerst einmal muss ich Dir meinen Respekt zollen, es ist Dir gelungen meine Fragen zu beantworten ohne sie zu beantworten. Ich bin ein bisschen Ratlos.


Der ultimative Fall ist der des Busmasterings.
Warum? ;) Der ist schnell und CPU-schonend, ja, aber es gibt auch einige konzeptionelle Nachteile wie das die Geräte die Speicherverwaltung der CPU (Paging) verstehen und nachahmen müssen (per Scatter-Gather-Listen).

Du übergibst der API also einen Zeiger auf ein Stück physischen Speicher, den die PCI-Karte sich per Busmastering holt.
Wie soll das gehen? Der Code hinter der API, also der PCI-Bus-Treiber, kennt ja nicht das konkrete PCI-Gerät also weiß er gar nicht was er mit dem Pointer machen muss (wie dieser Pointer dem PCI-Gerät übergeben werden muss bzw. wie diese Scatter-Gather-Listen aufgebaut sein müssen). Das weiß nur der eigentliche Geräte-Treiber.

Genau deswegen ist es auch unmöglich das Ansteuerungskonzept eines PCI-Gerätes auf ein Gerät zu übertragen das an einem Bus hängt der nicht mindestens über die selben Features, wie z.B. Busmastering, wie PCI verfügt und deswegen kann der Geräte-Treiber seine Hardware auch nur als PCI-Gerät ansprechen. Denn wenn wir ein funktional vergleichbares Gerät für z.B. USB entwickeln wird es zwangsläufig ein anderes Ansteuerungskonzept bekommen und deswegen nicht mehr mit dem selben Geräte-Treiber ansprechbar sein. Aus eben diesem Grund bin ich der Meinung das Busabstraktion kaum einen praktischen Nutzwert hat.

Ein ISA-Bustreiber kopiert die Daten dann mittels MMIO in die Karte.
Okay, das ist auf jeden Fall machbar aber es stellt sich die Frage: Warum soll man ein memcpy abstrahieren? Das Konzept des Speicher gibt es immer und wenn nicht dann gibt es auch keine MMIO-basierenden Geräte.

Ein USB-Bustreiber verpackt die Daten und schickt sie weg. Ein SPI-Bitbanging-Bustreiber fängt an, die Daten rauszutakten.
Ja, bei diesen Bussen ist der Aufruf einer API-Funktion absolut nötig und korrekt aber bei Bussen die direkt von der CPU erreichbar sind, z.B. ISA und PCI, ist der Funktionsaufruf IMHO eine Performance-Bremse (und im Fall von gerätespezifischen Busmastering auch völlig unmöglich).

Eine gute API (die ich nicht entwickelt habe) orientiert sich am besten (PCI), bezieht aber die Eigenheiten der anderen Dinge mit ein.
Ich erwarte ja gar nicht das Du hier eine komplette Bus-API entwickelst aber wenigstens mal ein Konzept grob darlegen wäre echt nicht schlecht. Ich habe jetzt einen ganzen Haufen an Gründen genannt warum ich der Meinung bin dass das gar nicht umsetzbar (und auch nicht praktikabel) ist und Du drückst Dich um ein konkretes Beispiel wie es Deiner Meinung nach funktionieren könnte. Das ist frustrierend. :cry:

Typsicherheit ist ein reines Implementationsdetail, noch dazu stark abhängig von der Programmiersprache. Außerdem: Typsicherheit wofür?
Also wenn Du als Programmierer nicht weist wofür Typsicherheit gut ist dann werde ich das wohl auch nicht erklären können. Sorry, wenn das reichlich arrogant klingt aber eigentlich sind wir uns doch einig das wir ein OS nicht mehr in Assembler programmieren wollen sondern in einer Hochsprache und die bringen alle mehr oder weniger Typsicherheit mit (einfach weil sich das als nützliches Programmierparadigma erwiesen hat).

Deine API definiert die Datentypen, die dort verwendet werden. Die müssen nicht identisch zu dem sein, was am Ende für die Hardware da ist!
Aber der Geräte-Treiber muss auch auf diese Informationen anständig zugreifen können. Der benötigt z.B. die genaue PCI-Geräte-ID um sich je nach Produktversion ein klein wenig anders verhalten zu können. Ein gutes Beispiel ist da der e1000-Treiber, diese HW gibt es in sehr vielen Varianten (z.B. mit Kupfer- oder Glasfaser-Anschluss) und trotz dessen dass das Grundansteuerungsprinzip immer gleich ist sind eben doch ein paar Details spezifisch zu erledigen. Es wäre Unsinn für jede e1000-Variante einen eigenen Treiber zu bauen sondern es ist viel zweckmäßiger einen Treiber für alle zu haben der dann eben an ein paar wenigen Stellen im Code eine Fallunterscheidung anhand der Geräte-ID macht.

Wie wäre es mit einer abstrakten 64-Bit langen 'Adresse' des Gerätes 'an einem Bus'?
Was genau meinst Du mit "abstrakt"? Zumindest in C gibt es keine abstrakten Datentypen.

Ich sehe einfach nicht wie Du das hinter einer einheitlichen API verstecken möchtest.
*seufz* Ich lass es. Bringt nichts.
Du hast ja auch noch gar nicht erklärt wie es denn funktionieren könnte. Nimm doch einfach das Beispiel von oben mit dem Übergeben von Daten an ein Gerät (einmal PCI mit Busmastering, einmal ISA mit MMIO und dazu noch USB oder SPI mit ner Sende-Funktion) und zeige wie diese einheitliche API konkret aussehen könnte und wie die von den jeweiligen Bus-Treibern umgesetzt werden müsste.

Du sollst doch nicht den NE2000-Baustein direkt an einen USB-Bus hängen. Du gibst ihm eine Umgebung, in der er lebt (Datenein-&ausgabe) und diese Umgebung hängst du an den USB.
Dann ist es aber kein NE2000 mehr sondern dann hast Du einen Umsetzer von einem neuen USB-basierten Interface nach NE2000 gebaut und der Geräte-Treiber muss mit diesem neuen Interface umgehen können. Daraus folgt das Du einen neuen Treiber (für Dein USB-Interface) programmieren musst (auch wenn Du sicher viele Code-Teile vom NE2000-Treiber übernehmen kannst) und daher bin ich der Meinung das die Bus-Abstraktion in diesem Fall schon mal nicht funktioniert hat. Klar könntest Du die IO-Zugriffe vom System alle abfangen lassen und die dann in einer Emulationsschicht für Dein USB-Interface umsetzen (in der Art wie das diese LPT-ISA-Sache macht) damit der alte NE2000-Treiber noch funktioniert aber das verstehe ich persönlich nicht unter Abstraktion (des entspricht IMHO der Virtualisierung).


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 18. February 2011, 14:44 »
Hallo,

Svenska, zuerst einmal muss ich Dir meinen Respekt zollen, es ist Dir gelungen meine Fragen zu beantworten ohne sie zu beantworten. Ich bin ein bisschen Ratlos.
Das liegt unter anderem daran, dass ich den PCI-Bus bei weitem nicht so gut kenne wie du. Eigentlich überhaupt nicht; ich kenne mich ein bisschen mit ISA, I²C und SPI aus. Deswegen beziehe ich die Dinge alle darauf und nicht auf PCI, was du ständig machst.

Der ultimative Fall ist der des Busmasterings.
Warum? ;) Der ist schnell und CPU-schonend, ja, aber es gibt auch einige konzeptionelle Nachteile wie das die Geräte die Speicherverwaltung der CPU (Paging) verstehen und nachahmen müssen (per Scatter-Gather-Listen).
Busmastering ist der eleganteste Fall. Wenn die Busabstraktion das abbilden kann, ist damit ein Zero-Copy möglich. Wenn die höchste Abstraktionsstufe "nur" MMIO hergibt, dann wirst du damit PCI-Geräten keinen Gefallen tun. Das meinte ich. Dazu muss man aber alle Bussysteme kennen und den PCI kenne ich (abgesehen von ein paar Features) nunmal nicht, schon garnicht im Detail.

Genau deswegen ist es auch unmöglich das Ansteuerungskonzept eines PCI-Gerätes auf ein Gerät zu übertragen das an einem Bus hängt der nicht mindestens über die selben Features, wie z.B. Busmastering, wie PCI verfügt und deswegen kann der Geräte-Treiber seine Hardware auch nur als PCI-Gerät ansprechen.
Da kann ich nicht viel zu sagen. Höchstens, dass deine API ja nach der Ansteuerung über den PCI-Bus modelliert sein kann.

Denn wenn wir ein funktional vergleichbares Gerät für z.B. USB entwickeln wird es zwangsläufig ein anderes Ansteuerungskonzept bekommen und deswegen nicht mehr mit dem selben Geräte-Treiber ansprechbar sein.
Naja, es gibt kaum Hardware, die für mehrere völlig verschiedene Bussysteme existiert und wenn sie das tut, dann weiß der Treiber das in der Regel auch (schließlich muss er die Hardware evtl. entsprechend konfigurieren können => NE2000 als NE1000 in einem 8-Bit-Steckplatz).

Wenn die Hardware halt sowohl Speicher- als auch Portzugriffe benötigt, dann wird der Treiber schon so strukturiert sein, dass er zwei Funktionen "send_mem" und "send_io" enthält, die dann entsprechende Funktionen der Busabstraktions-API aufruft oder, wenn du sowas nicht hast, direkt arbeitet. Und wenn die USB-Variante jetzt zwei Endpoints anbietet, die als Speicher- bzw. Portendpunkt funktionieren, dann sind das im Treiber nur wenige Zeilen und ein switch, um das zu implementieren.

Wenn du allerdings keine gemeinsame Busabstraktion hast (sondern dein HAL eine API für PCI implementiert, eine ganz andere API für USB usw.), dann ist das schon wesentlich umständlicher und läuft am Ende auf einen völlig neuen Treiber hinaus, mit identischem, mehrfach vorhandenem Code usw; es sei denn, du faktorisierst den gemeinsamen Anteil heraus. Das ist eine Menge Arbeit und wird meist nicht (oder sehr viel später) gemacht.

Bei Linux sieht man das jetzt wieder für die r300g- und r600g-Treiber, wo komplett verschiedene Shaderimplementierungen benutzt werden und jetzt der r300g-Anteil (den es schon vorher gab und der schneller/besser ist) rausfaktorisiert wird, um ihn in r600g nutzen zu können.

Das Ziel, dass ein identischer Treiber auf Hardware funktioniert, für die er nie geschrieben wurde, ist logischerweise ein Theoretisches. Praktisch hast du sowieso Errata, die du berücksichtigen musst.

Aus eben diesem Grund bin ich der Meinung das Busabstraktion kaum einen praktischen Nutzwert hat.
Das ist mit jeder Theorie so. Am Ende sieht man, was sich bewährt hat und was nicht - und zwar im Kontext von Aufwand gegen Nutzen. Mit dem Argument könnte man übrigens Microkernel komplett einstampfen, schließlich sind alle bekannten, großen System Monolithen (Linux, *BSD, Solaris, Win 9x) oder zumindest nahezu monolithisch (NT als Hybridkernel). Dennoch ist der Microkernel ein schönes theoretisches Konstrukt und funktioniert auch.

Ein ISA-Bustreiber kopiert die Daten dann mittels MMIO in die Karte.
Okay, das ist auf jeden Fall machbar aber es stellt sich die Frage: Warum soll man ein memcpy abstrahieren? Das Konzept des Speicher gibt es immer und wenn nicht dann gibt es auch keine MMIO-basierenden Geräte.
Warum gibt es Datentypen wie "size_t", wenn man auch einfach einen "int" nutzen kann? Zahlen sind schließlich Zahlen.

Der Sinn eines HAL ist es, die Hardware vollständig vom Gerätetreiber abzutrennen. Praktisch ist das nicht umsetzbar, also entfernt man sich mehr oder weniger von diesem Konzept und der HAL wird dementsprechend dünner und du hast mehr gemeinsamen Code in den Treibern.

Ein USB-Bustreiber verpackt die Daten und schickt sie weg. Ein SPI-Bitbanging-Bustreiber fängt an, die Daten rauszutakten.
Ja, bei diesen Bussen ist der Aufruf einer API-Funktion absolut nötig und korrekt aber bei Bussen die direkt von der CPU erreichbar sind, z.B. ISA und PCI, ist der Funktionsaufruf IMHO eine Performance-Bremse (und im Fall von gerätespezifischen Busmastering auch völlig unmöglich).
Gerätespezifisches Busmastering ist, soweit ich das verstehe, ohnehin stark vom verwendeten Bus abhängig. Du kannst Treiber, die sowas nutzen, ja auch unterhalb des HAL ansiedeln und einen Durchgriff durchs Layering definieren. Diese Treiber sind dann halt nicht portabel (und können es auch nicht sein).

Ich erwarte ja gar nicht das Du hier eine komplette Bus-API entwickelst aber wenigstens mal ein Konzept grob darlegen wäre echt nicht schlecht. Ich habe jetzt einen ganzen Haufen an Gründen genannt warum ich der Meinung bin dass das gar nicht umsetzbar (und auch nicht praktikabel) ist und Du drückst Dich um ein konkretes Beispiel wie es Deiner Meinung nach funktionieren könnte. Das ist frustrierend. :cry:
Ich schreibe halt kein OS, du schon. ;-)

Ich habe keine Ahnung von PCI und weiß u.a. nicht, wie man Scatter-Gather konkret benutzt (nur, wozu es gut ist). Eine Abstraktion für kleine Busse könnte ich vielleicht bauen, aber ohne PCI wird dir das nichts nutzen. Mal abgesehen davon sind Gedankenexperimente ohne Implementation wertlos.

Typsicherheit ist ein reines Implementationsdetail, noch dazu stark abhängig von der Programmiersprache. Außerdem: Typsicherheit wofür?
Also wenn Du als Programmierer nicht weist wofür Typsicherheit gut ist dann werde ich das wohl auch nicht erklären können.
Du hast mich missverstanden.

Wofür (für welchen Teil) soll die Typsicherheit denn sein? Zwischen Treiber und Bus-API (HAL)? Die ist zwingend gegeben, schließlich definiert deine API ja den Datentyp. Zwischen HAL und Hardware? Da arbeitest du eh nur mit Zahlen, und für die Typsicherheit muss der HAL sorgen. Zwischen Treiber und Hardware? Da liegt der HAL zwischen.

Das zweite ist, dass Typsicherheit ein Implementationsteil ist, der bei der Definition einer API keine Rolle spielen sollte - dort definierst du Datentypen. Der Compiler, der dir sagt, dass die nicht zusammenpassen, ist in der Betrachtung außen vor.

Der benötigt z.B. die genaue PCI-Geräte-ID um sich je nach Produktversion ein klein wenig anders verhalten zu können. Ein gutes Beispiel ist da der e1000-Treiber, diese HW gibt es in sehr vielen Varianten (z.B. mit Kupfer- oder Glasfaser-Anschluss) und trotz dessen dass das Grundansteuerungsprinzip immer gleich ist sind eben doch ein paar Details spezifisch zu erledigen.
Sehe ich kein Problem. Wenn du eine 64-Bit-Zahl nimmst, und für PCI-Geräte sind die oberen vier Bytes halt null und die unteren 4 Bytes sind Hersteller/Gerätenummer, dann hat der Treiber die IDs ja trotzdem vorrätig. Ansonsten hast du halt ein Typfeld (PCI=0, USB=1, SPI=2, ...) und die abstrakte Geräte-ID sieht halt, je nach Bus, verschieden aus - ist aber trotzdem konstant.

Du hast ja auch noch gar nicht erklärt wie es denn funktionieren könnte. Nimm doch einfach das Beispiel von oben mit dem Übergeben von Daten an ein Gerät (einmal PCI mit Busmastering, einmal ISA mit MMIO und dazu noch USB oder SPI mit ner Sende-Funktion) und zeige wie diese einheitliche API konkret aussehen könnte und wie die von den jeweiligen Bus-Treibern umgesetzt werden müsste.
Für PCI mit Busmastering muss ich passen. Für den Rest hast du eine Funktion zum Senden und jeweils mindestens eine Implementation in den Bustreibern:

Code: (Prototyp) [Auswählen]
int send_data(struct bus_device dev, int endpoint, void *data, int len, int dest_offset);
Code: (ISA/MMIO) [Auswählen]
int send_data_isa(struct bus_device *dev, int endpoint, void *data, int len, int dest_offset) {
  if (endpoint != 0) return -EENDPOINT; /* endpoint muss null sein */

  memcpy(dev->blackbox->mmio_base+dest_offset, data, len);
}

Code: (USB) [Auswählen]
int send_data_usb(struct bus_device *dev, int endpoint, void *data, int len, int dest_offset) {
  if (!(dev->blackbox->ep_dir &= USB_DIR_WRITE)) return -EENDPOINT; /* endpoint muss schreibbar sein */
  if (dest_offset != 0) return -EDEST; /* usb-transfers haben keinen ziel-offset */

  /* sendefunktion des hostcontrollers, untersucht den typ des endpoints und tut das richtige */
  /* blackbox ist ein bus-abhängiger container, auf den der gerätetreiber nicht zugreifen sollte */
  ehci_send_data(dev->blackbox, data, len);
}

Code: (I²C) [Auswählen]
int send_data_i2c(struct bus_device *dev, int endpoint, void *data, int len, int dest_offset) {
  int i;

  if (endpoint != 0) return -EENDPOINT;

  i2c_send_wire( I2C_DIR_WRITE | dev->id, 1 ); /* adresse senden; 1 = warte auf  ACK */
  for( i = 0; i < len-1; i++ )
    i2c_send_wire( data+i, 1 ); /* sende daten mit ACK (fortsetzung folgt) */

  i2c_send_wire( data+len-1, 0); /* sende daten ohne ACK (letztes paket) */
}

I²C ist bei mir schon eine Weile her, ob das mit dem ACK/NACK stimmt, weiß ich gerade nicht. Grundsätzlich hast du hierbei eine Black Box, die den Status für den Bus-Treiber (HAL) enthält, und der nur von den jeweiligen Bustreibern verwaltet wird (werden sollte). Und das ganze hier ist ein Gedankenexperiment.

Ein Treiber darf trotzdem darauf zugreifen, gibt damit aber seine Portabilität auf, ist potentiell abhängig von der Version des Bustreibers - und hat damit die Möglichkeit, direkt auf das Gerät zuzugreifen und Dinge zu tun, die von der Abstraktion nicht erfasst werden.

Um eine Hardware zum Laufen zu kriegen, sollte jeder Treiber mit der Abstraktion auskommen können. Ein maschinenspezifischer Treiber, der irgendwelche tollen Features nutzt, kann sich ja auch direkt mit dem Bustreiber unterhalten, statt über den HAL zu gehen.

Daraus folgt das Du einen neuen Treiber (für Dein USB-Interface) programmieren musst (auch wenn Du sicher viele Code-Teile vom NE2000-Treiber übernehmen kannst) und daher bin ich der Meinung das die Bus-Abstraktion in diesem Fall schon mal nicht funktioniert hat.
Der Treiber muss darüber informiert sein, dass die Ansteuerung hier anders läuft. Also wirfst du in die Sendefunktionen eine Unterscheidung rein, die je nach Zugriff den richtigen Endpoint auswählt und der Treiber funktioniert dann auch mit dieser Hardware. Ich brauche keinen neuen NE2000-Treiber schreiben und ich brauche vor allem nicht aus dem vorhandenen NE2000-Treiber die Einzelteile raussuchen, die ich weiternutzen kann. Siehe oben.

Klar könntest Du die IO-Zugriffe vom System alle abfangen lassen und die dann in einer Emulationsschicht für Dein USB-Interface umsetzen (in der Art wie das diese LPT-ISA-Sache macht) damit der alte NE2000-Treiber noch funktioniert aber das verstehe ich persönlich nicht unter Abstraktion (des entspricht IMHO der Virtualisierung).
Das ist auch ein böser Hack und keine schöne Lösung.

Gruß

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 19. February 2011, 14:49 »
Hallo,


dass ich den PCI-Bus bei weitem nicht so gut kenne wie du. Eigentlich überhaupt nicht
Ja, das scheint  mir auch so. Ich könnte Dir ja jetzt die PCI-Spec, die PCI-Express-Spec und die HyperTransport-Spec ans Herz legen damit Du einen Eindruck von PCI und seinen Verwandten bekommst. Damit Du dann noch eine gute Vorstellung davon bekommst wie man mit diesen Möglichkeiten am besten umgeht wären die AHCI-Spec, die EHCI-Spec, die xHCI-Spec und das Driver-Developer-Manual für die e1000-Serie ein guter Rat. Das sind dann so etwa 4000 bis 5000 Seiten aber das Wochenende hat ja gerade erst angefangen. ;)
Und selbst wenn Du das alles komplett gelesen hast (was ich nicht getan habe) hab ich immer noch fast 10 Jahre Vorsprung was die Entwicklung von PCI-Hardware und deren Ansteuerung per Software betrifft.

Ja, ich beziehe mich sehr oft auf PCI, einfach weil dass das ist womit ich mich am besten auskenne und natürlich weil der PCI die mächtigsten Möglichkeiten bietet. Trotzdem bin ich der Meinung selbst wenn man PCI gut im Griff hat heißt das noch nichts für SPI, I²C oder USB da diese Busse anders arbeiten. Bei PCI, ISA und der Gleichen hat man üblicherweise ne Reiche an Registern zur Verfügung (unabhängig davon ob die nun im IO-Adressraum oder im Speicher-Adressraum liegen) über die man die Hardware steuert und deren Status ermittelt. Diese Register lassen sich beliebig (vor allem in beliebiger Reihenfolge) lesen und schreiben. Bei den eher Byte-Strom orientierten Bussen wie eben UBS, SPI usw. versucht man das Ansteuerungskonzept auf Paketen aufzubauen in denen meistens ein Job kodiert ist und auch gleich die Nutzdaten mit dabei sind. Der Paket-Overhead ist meistens zu groß als das man für jedes Bisschen eine eigene Anfrage an das Gerät schickt. Gerade dieser grundlegende Unterschied macht es meiner persönlichen Meinung nach unsinnig diese 2 grundverschiedenen Philosophien in einen Topf zu werfen. Ich hätte kein Problem damit wenn man versucht SPI, I²C, OneWire und Ähnliche Busse hinter einer gemeinsamen Abstraktion zu verbergen, schon weil es für alle sehr ähnliche Geräte (kleine Sensoren für Temperatur u.ä., EEPROMs, AD/DA-Wandler u.a.m.) gibt die auch oft sehr ähnlich angesteuert werden. Hier würde es einfach Sinn ergeben wenn man da Gemeinsamkeiten zusammen fasst, es gibt in dieser Bus-Kategorie eben auch viele Gemeinsamkeiten und nur wenige Unterschiede (die sich vor allem im physischen Protokoll finden). Aber dort Dinge wie ISA, EISA, NuBus, MCA, VLB oder auch PCI mit rein zu packen ist meiner Meinung nach von vorn herein zum Scheitern verurteilt einfach weil die Gemeinsamkeiten zwischen diesen 2 Welten zu wenige bzw. kaum existent sind. Dinge wie Busmastering und MMIO sind bei SPI, I²C usw. einfach nicht realisierbar und die Paketorientierung wird man bei ISA usw. nicht benutzen weil man ja Register hat die direkt von der CPU (bzw. der darauf laufenden SW) angesprochen werden können.

Genau deswegen ist es auch unmöglich das Ansteuerungskonzept eines PCI-Gerätes auf ein Gerät zu übertragen das an einem Bus hängt der nicht mindestens über die selben Features, wie z.B. Busmastering, wie PCI verfügt und deswegen kann der Geräte-Treiber seine Hardware auch nur als PCI-Gerät ansprechen.
Da kann ich nicht viel zu sagen. Höchstens, dass deine API ja nach der Ansteuerung über den PCI-Bus modelliert sein kann.
PCI gibt das Ansteuerungskonzept für die Geräte nicht vor, es werden nur gewisse Rahmenbedingungen vorgegeben. Wie das Busmastering dann realisiert wird ist von PCI-Gerät zu PCI-Gerät völlig anders. Deswegen kann es für PCI keine zentrale Instanz geben die das Busmastering für verschiedene oder gar unbekannte Geräte ansteuert und deswegen kann es für Deinen send_data-Code auch keine PCI-Version geben.

Wenn die Hardware halt sowohl Speicher- als auch Portzugriffe benötigt, dann wird der Treiber schon so strukturiert sein, dass er zwei Funktionen "send_mem" und "send_io" enthält, die dann entsprechende Funktionen der Busabstraktions-API aufruft oder, wenn du sowas nicht hast, direkt arbeitet. Und wenn die USB-Variante jetzt zwei Endpoints anbietet, die als Speicher- bzw. Portendpunkt funktionieren, dann sind das im Treiber nur wenige Zeilen und ein switch, um das zu implementieren.
Sorry, wenn ich das so direkt frage aber viel Erfahrung mit Treiberprogrammierung hast Du nicht oder? In vielen alten Treibern gibt es write_port() und read_port() und diese Funktionen enthalten effektiv nur einen Assemblerbefehl und werden deswegen geinlined (falls es nicht eh bloß Makros sind). Diese Funktionen oder Makros werden demzufolge sehr häufig benutzt, schließlich sind für viele Aufgaben mehrere Register-Zugriffe erforderlich. Wenn man versuchen würde dieses Konzept 1:1 auf einen Byte-Strom orientierten Bus wie USB zu übernehmen käme da nichts sinnvolles bei raus. Dann wären write_port() und read_port() richtige Funktionen die jedes mal einen kompletten Kommunikationsvorgang zwischen Geräte-Treiber und Hardware benötigen und dass das nicht sonderlich zielführend ist dürfte klar sein.

Wenn du allerdings keine gemeinsame Busabstraktion hast (sondern dein HAL eine API für PCI implementiert, eine ganz andere API für USB usw.), dann ist das schon wesentlich umständlicher und läuft am Ende auf einen völlig neuen Treiber hinaus, mit identischem, mehrfach vorhandenem Code usw; es sei denn, du faktorisierst den gemeinsamen Anteil heraus. Das ist eine Menge Arbeit und wird meist nicht (oder sehr viel später) gemacht.
Ja, da hast Du recht. Aber wo stört das? Es gibt keine Geräte die bei PCI und USB in identischer Form vorliegen also gibt es einfach keinen Bedarf für eine gemeinsame API.
Du hast ein paar mal die NE2000 als Beispiel angeführt aber dabei unterschlagen das es das Ansteuerungskonzept, das nur auf ein paar IO-Registern und einem Interrupt basiert, auf all den Bussen, für die es die NE2000 gab, auch identisch zur Verfügung steht. Unter den direkt CPU-erreichbaren Bussen auf der PC-Plattform sind IO-Register, MMIO und Interrupts quasi ne Art kleinster gemeinsamer Nenner und deswegen kann ein Treiber alle NE2000-Implementierungen ansteuern ohne das er sich dafür interessieren muss an welchem Bus diese NE2000 nun konkret hängt. Aber eine PCI-Version der NE2000 funktioniert z.B. nur wenn der zugewiesene Interrupt nicht geshared wird, damit können zumindest die älteren DOS-Treiber nicht umgehen. Für die NE2000 ist eine Busabstraktion quasi nicht erforderlich, von der Beschaffung der IO-Basis-Adresse und der IRQ-Nummer mal abgesehen. Für USB, SPI, I²C, OneWire usw. (also alle Busse die die CPU nicht direkt erreicht) ist immer eine Busabstraktion erforderlich schließlich will der Geräte-Treiber sich nicht auch noch um die konkrete Realisierung der physischen Busansteuerung kümmern müssen. Es wäre ja wohl auch Unsinn wenn der Treiber für USB-Mäuse sich mit OHCI, UHCI, EHCI und xHCI auskennen müsste, das ist der Job für die USB-Abstraktionsschicht. Bei ISA, PCI usw. sorgt die CPU und der Chipsatz dafür das IN und OUT immer gleich funktionieren.

Bei Linux sieht man das jetzt wieder für die r300g- und r600g-Treiber, wo komplett verschiedene Shaderimplementierungen benutzt werden und jetzt der r300g-Anteil (den es schon vorher gab und der schneller/besser ist) rausfaktorisiert wird, um ihn in r600g nutzen zu können.
Dieses Problem liegt aber sicher nicht an einer fehlenden Busabstraktion. Da wurde wohl irgendetwas anderes in den Treibern ungünstig realisiert und das muss wohl nun korrigiert werden. Wenn zwischen 2 unterschiedlichen aber ähnlichen Geräten eine gewisse Menge an Gemeinsamkeiten vorhanden sind dann ist es wohl auch geschickt dafür gemeinsamen Quell-Code zu benutzen.

Um es noch mal ganz klar zu schreiben: für reale Gemeinsamkeiten sollte man auch meiner Meinung nach gemeinsamen Code nutzen.
Das Problem zwischen ISA/EISA/MCA/PCI auf der einen Seite und USB/SPI/I²C/OneWire auf der anderen Seite ist nur das dort keine Gemeinsamkeiten vorhanden sind.
Ein gutes Beispiel für Gemeinsamkeiten ist z.B. das MII-Interface bei Ethernet-Controllern, über dieses in der IEEE-802.3-Spec genormte Interface wird z.B. die Geschwindigkeit und Vollduplex/Halbduplex ausgehandelt. Daher lagert man das üblicherweise in eine extra Library aus die der Ethernet-Geräte-Treiber benutzt um eben diese Informationen zu bekommen mit denen er dann selber seinen Ethernet-Controller passend konfiguriert und zwar unabhängig davon ob der Ethernet-Controller per ISA, PCI, USB oder anders angeschlossen ist. Damit diese MII-Library arbeiten kann muss der Geräte-Treiber aber wieder ein abstraktes/vordefiniertes Interface anbieten über das die MII-HW-Komponente durch den spezifischen Ethernet-Controller hindurch erreichbar ist (wie die physische Verbindung zwischen dem Ethernet-Controller und der MII-Komponente aussieht ist nicht fest vorgegeben, es müssen ja auch nicht unbedingt 2 einzelne Chips sein denn es gibt auch Ethernet-Controller wo der Phy schon mit drin ist).

Gerätespezifisches Busmastering ist, soweit ich das verstehe, ohnehin stark vom verwendeten Bus abhängig. Du kannst Treiber, die sowas nutzen, ja auch unterhalb des HAL ansiedeln und einen Durchgriff durchs Layering definieren. Diese Treiber sind dann halt nicht portabel (und können es auch nicht sein).
Vom Bus ist das Busmastering eher weniger abhängig, der Bus muss das nur grundsätzlich ermöglichen. Mann könnte PCI auch gegen einen ganz anderen Bus austauschen, solange der neue Bus auch Busmastering, MMIO, Interrupts und P&P unterstützt sollten die meisten Geräte-Treiber ohne eine Änderung funktionieren (mal von Dingen abgesehen die von einer konkreten Device-ID anhängig sind). Schon wenn Du mal PCI mit seinen Verwandten vergleicht stellt man schnell fest das die eigentlich nur das höhere Protokoll gemeinsam haben. Ich denke eine der Stärken von PCI ist es das Konzept von der physischen Implementierung zu trennen. Wenn man dann mal das P&P-Zeugs von PCI mit USB vergleicht wird man auch dort sehr viele Gemeinsamkeiten feststellen. Zumindest auf PCI-Express, mit seinen Bridges und reinen Punkt-zu-Punkt-Verbindungen sollte man das USB-P&P übertragen können ohne das die Geräte-Treiber das merken würden (so lange die selben Informationen wie Vendor/Device-ID, BARs usw. zur Verfügung stehen).

Wofür (für welchen Teil) soll die Typsicherheit denn sein? Zwischen Treiber und Bus-API (HAL)? Die ist zwingend gegeben, schließlich definiert deine API ja den Datentyp. Zwischen HAL und Hardware? Da arbeitest du eh nur mit Zahlen, und für die Typsicherheit muss der HAL sorgen. Zwischen Treiber und Hardware? Da liegt der HAL zwischen.
Du hast mich wohl auch missverstanden. Ich meinte die Typsicherheit für den Geräte-Treiber bei der Kommunikation mit dem HAL. Wenn Du Dinge wie die Geräte-Adresse in einen 64Bit-Datentyp verpackst dann muss jeder Treiber das wieder so entpacken wie das für den Bus an dem das Gerät hängt vom HAL benutzt wird. Also wenn ein Geräte-Treiber für ein PCI-Gerät die Bus-Nummer/Device-Nummer/Function-Nummer haben will dann muss er diesen unspezifischen 64Bit-Typ korrekt zerlegen können und das selbe Problem hat auch der Geräte-Treiber für I²C-Geräte der die 7Bit-Adresse haben möchte. Hier ist es IMHO besser wenn der PCI-Geräte-Treiber direkt eine PCI-spezifische HAL-Funktion fragen kann aus der das gewünschte Trio typsicher raus kommt und das selbe für USB wo eine implementierungsspezifische Bus-Nummer und eine 7Bit-Geräte-Adresse als Ergebnis kommen.

Sehe ich kein Problem. Wenn du eine 64-Bit-Zahl nimmst, und für PCI-Geräte sind die oberen vier Bytes halt null und die unteren 4 Bytes sind Hersteller/Gerätenummer, dann hat der Treiber die IDs ja trotzdem vorrätig. Ansonsten hast du halt ein Typfeld (PCI=0, USB=1, SPI=2, ...) und die abstrakte Geräte-ID sieht halt, je nach Bus, verschieden aus - ist aber trotzdem konstant.
Ich sehe da ein Problem. Du kannst dann dieses Detail nie wieder am HAL ändern. Oder der HAL muss für jeden Bus dafür eine Entpack-Funktion anbieten aber dann hätte man sich die gemeinsame Basis IMHO auch gleich sparen können.

Für PCI mit Busmastering muss ich passen. Für den Rest hast du eine Funktion zum Senden und jeweils mindestens eine Implementation in den Bustreibern:

Code: (Prototyp) [Auswählen]
int send_data(struct bus_device dev, int endpoint, void *data, int len, int dest_offset);[......]
Hm, find ich irgendwie nicht so toll. 'endpoint' und 'dest_offset' sind IMHO redundant da wohl kaum beides benötigt wird. Für PCI muss ich ebenfalls passen, einfach weil das so nicht geht.

Um eine Hardware zum Laufen zu kriegen, sollte jeder Treiber mit der Abstraktion auskommen können. Ein maschinenspezifischer Treiber, der irgendwelche tollen Features nutzt, kann sich ja auch direkt mit dem Bustreiber unterhalten, statt über den HAL zu gehen.
Also "zum Laufen zu kriegen" wird oft nicht genug sein, schließlich hat der HW-Designer viel Zeit und Mühe investiert um das Gerät genau so zu bauen damit es maximale Performance/Funktionalität/sonstwas erreicht und der Treiber-Programmierer wird es nicht gerne sehen wenn dann nur ein Bruchteil davon mit dem HAL funktioniert. Wenn die Treiber-Programmierer sich daran gewöhnt haben am HAL vorbei zu entwickeln dann hätte der OS-Programmierer diesen HAL auch gleich ganz bleiben lassen können.

Der Treiber muss darüber informiert sein, dass die Ansteuerung hier anders läuft. Also wirfst du in die Sendefunktionen eine Unterscheidung rein, die je nach Zugriff den richtigen Endpoint auswählt und der Treiber funktioniert dann auch mit dieser Hardware. Ich brauche keinen neuen NE2000-Treiber schreiben und ich brauche vor allem nicht aus dem vorhandenen NE2000-Treiber die Einzelteile raussuchen, die ich weiternutzen kann. Siehe oben.
Ich weiß ja nicht von welcher Ebene Deine "Sendefunktion" ist aber wenn die sich z.B. auf die Ethernet-Frames bezieht dann muss ich Dir sagen das der eigentliche Treiber erst da drunter liegt. Ein Ethernet-Treiber macht nichts anderes als Ethernet-Frames zu versenden und zu empfangen, ist also eine Art packetorientierte serielle Schnittstelle (nur ein kleines bisschen schneller und damit die CPU nicht zum PIO-Flaschenhals wird, so wie bei nem normalen UART, benutzt man heutzutage eben Busmastring wogegen die NE2000 nichts weiter als ein besserer UART mit altbackenem PIO-Interface war).

Das ist auch ein böser Hack und keine schöne Lösung.
Na wenigstens etwas über das wir einer Meinung sind. Das ist doch schon mal ein Anfang. ;)


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen