Ataris Festplattentreiber »AHDI« wurde in der Vergangenheit ständig weiterentwickelt. Trotzdem blieben Wünsche - gerade bei Wechselplattenunterstützung - offen. Der neue Standard »XHDI« will hier weiterhelfen.
Auf dem Gebiet der Harddisktreiber gibt es zwar laufend erfreuliche Detailverbesserungen bei Atari (zuletzt mit AHDI 5) - leider hapert es aber an entsprechender Dokumentation und wohl auch der Zeit und dem Willen an konsequenter Weiterentwicklung. Was tut man, wenn man Standardisierung braucht, von Atari aber nichts kommt? Richtig: man definiert selbst eine Spezifikation (Sie erinnern sich doch an »XBRA«?). In diesem Fall haben sich Programmierer von Treibern und Utilities zusammengetan, um die »XHDI«-Spezifikation zu erarbeiten.
Da es sich im folgenden um eine Spezifikation handelt, die naturgemäß möglichst präzise formuliert sein will, ist die sehr technische Sprache unumgänglich.
Wie unschwer am Namen (»eXtended HardDisk Interface«) zu erkennen ist, soll die XHDI-Spezifikation die Möglichkeiten der Kommunikation mit Treibern für blockorientierte Massenspeicher verbessern. Ausgangspunkt war die Überlegung, einige zusätzliche Eigenschaften, die viele Treiber bereits haben, über eine dokumentierte Schnittstelle nach außen zu führen. Dies sollte speziell Virtual-Memory-Programmen die Möglichkeit geben, Wechselplatten zu verriegeln (wer wollte schon, daß die Swap-Partition während des Swappens entfernt werden kann ... ).
Mit fortschreitender Diskussion hat sich herausgestellt, daß auch die durch die PUN_INFO-Struktur bereitgestellten Informationen nicht immer ausreichen und daher über die XHDI-Spezifikation erweitert werden sollten. Einige Gründe: Die PUN_INFOStruktur hat nur Platz für 16 Geräte, obwohl das BIOS (und einige GEMDOS-Aufsätze) 32 Geräte erlauben. Die Installation mehrerer AHDI-kompatibler Treiber im System ist nicht möglich. Und schließlich: die Atari-Definition von Gerätenummern geht davon aus, daß immer nur LUN 0 eines ACSI- oder SCSI-Targets benutzt wird.
Der Sinn und Zweck der XHDI-Spezifikation (in der Version 1.00, Erweiterungen sind natürlich denkbar) sieht damit so aus: Flexiblerer und umfassenderer Zugang zu den Informationen über die einzelnen Geräte sowie Unterstützung erweiterter Treiberfunktionen wie Stopp/Start oder Verriegeln/ Entriegeln. Völlig neue Anforderungen an Treiber festzulegen, wäre unsinnig: Die XHDI-Spezifikation soll sich nach Möglichkeit auf einfache Weise in bestehende Treiber integrieren lassen.
Die XHDI-Systemerweiterung wird über einen Cookie mit der Kennung »XHDI« (wen wundert's?) installiert. Der Cookiewert ist ein Zeiger auf einen Funktionsdispatcher. Zur Absicherung steht vor der Routine die Long-Konstante $27011992. Der Wert des Cookies kann sich im laufenden Betrieb ändern (wg. Zweitinstallation). Daher muß man gegebenenfalls (z. B. in Accessories) den Cookie vor jedem Aufruf erneut abfragen!
Alle Funktionen müssen im Supervisor-Modus aufgerufen werden. Das Verhalten für Aufrufe im User-Modus ist undefiniert. Bis auf »D0« werden keine Prozessorregister verändert. Undefinierte Opcodes führen zur Fehlermeldung »EINVFN«. Für die Parameterübergabe gilt die GEMDOS-Übergabe-Konvention. Alle Parameter werden auf dem Stack abgelegt (der Opcode als 16-Bit-Wert), das Ergebnis wird in D0 zurückgeliefert.
Einige Funktionsaufrufe - insbesondere »XHReadWrite()« - können zum Aufruf von BIOS- oder XBIOS-Routinen im Betriebssystem und damit zur Aktivierung des »Critical Error Handler« führen. Im Zweifel muß »CEH« also vom Aufrufer abgeschaltet werden.
An Datentypen werden verwendet: UWORD (16-Bit, unsigned), LONG (32-Bit, signed), ULONG (32-Bit, unsigned), char * (32-Bit, Zeiger auf eine nullterminierte Zeichenkette).
Geräte werden über die Major- und Minor-Gerätenummer identifiziert. Für die Major-Gerätenummer sind zunächst die Werte 0..7 (Platten am ACSI-Bus mit Atari-kompatiblen Befehlssatz), 8..15 (Platten am SCSIBus), 16..63 (Erweiterungen laut AHDI-PUN-INFOStruktur, Feld: pun[]) und 64 (Gerät am Floppycontroller) definiert. Die Werte von 65 bis 255 sind für künftige Erweiterungen reserviert.
Die Definition der Minor-Gerätenummer ist von der Major-Gerätenummer abhängig. Bei Platten am ACSI- oder SCSI-Port handelt es sich um die »Logical Unit Number« (LUN) der Platte, für den Floppycontroller um die Gerätenummer des Diskettenlaufwerks. Wiederum sind Werte zwischen 0 und 255 erlaubt.
Wie schon erwähnt, sollen spezielle Programme die Möglichkeit bekommen, bestimmte Geräte für sich zu reservieren. Dazu ist einerseits eine spezielle Funktion erforderlich (»XHReserve()«), andererseits wird ein Mechanismus benötigt, mit dem sich Programme Zugriff auf diese reservierten Geräte beschaffen können. Dies geschieht über den »key« (einem 16Bit-Schlüssel, ermittelt von »XHReserve()«, oder 0, wenn das Gerät nicht reserviert wurde oder der Schlüssel nicht bekannt ist).
In Abb. 1 finden Sie eine Übersicht über alle bislang definierten XHDI-Kommandos. Abb. 2 zeigt die bislang definierten Return-Codes. Man beachte dabei, daß jeder Return-Wert kleiner 0 eine Fehlermeldung darstellt. Für Geräte, die nicht ACSI- oder SCSI-kompatibel sind (wie z.B. die IDEPlatte im ST-Book) müssen also erst noch bestimmte Fehlercodes definiert werden.
Kommen wir nun zu den einzelnen Funktionen:
»XHGetVersion()« liefert die Protokollversion zurück. Formatbeispiel: $0119 ist Version 1.19 (das Format ist identisch mit dem bei der TOS-Versionsnummer benutzten). Diese Version der XHDISpezifikation hat die Versionsnummer $0100.
»XHInqTarget()« liefert Informationen über das angegebene Gerät (in »device_flags«: ein Attributvektor, in »product_name«: optional die Produktbezeichnung des Geräts). Mit »XHReserve()« vorgenommene Reservierungen werden dabei berücksichtigt. In »block_size« wird die Blockgröße für das Gerät (für »XHReadWrite()« sehr wichtig) zurückgeliefert (normalerweise 512). Die »device_flags« teilen mit, welche besonderen Eigenschaften das Gerät hat (ein gesetztes Bit steht dabei für eine vorhandene Eigenschaft). Zunächst sind definiert:
Alle weiteren Bits sind reserviert und sollten vom Treiber auf Null gesetzt werden. In »product_name« wird die Produktbezeichnung des Geräts (max. 33 Zeichen inkl. Leerzeichen) vermerkt. Falls die Information nicht verfügbar ist, wird eine Zeichenkette der Länge Null zurückgeliefert. Anmerkung: für »block_size«, »device_flags« und »product_name« dürfen natürlich auch Nullzeiger übergeben werden.
»XHReserve()« reserviert ein Gerät bzw. gibt es wieder frei. Auf reservierte Geräte kann nur bei Angabe des korrekten Schlüssels per »XHLock()«, »XHStop()« oder »XHEject()« zugegriffen werden. Sinn der Sache: Man möchte nicht, daß man eine Wechselplatte per CPX-Modul entriegeln kann, nachdem sie gerade von einer virtuellen Speicherverwaltung verriegelt worden ist. Dies sollte nur die Speicherverwaltung selbst können. Beim Reservieren des Geräts wird im Erfolgsfall ein 16-Bit-Schlüssel zurückgeliefert. Dieser Schlüssel muß bei allen weiteren Zugriffen auf das Gerät sowie beim Wiederfreigeben angegeben werden. »do_reserve« gibt an, ob das Gerät reserviert (1) oder wieder freigegeben werden soll (0). »key« wird bei dieser Funktion nur bei der Freigabe benutzt.
»XHLock()« verriegelt bzw. entriegelt den Auswurfknopf eines Geräts. Der Treiber hat sich darum zu kümmern, ob dieser Befehl an das Gerät weitergeleitet wird oder nicht (falls das Medium nicht verriegelbar ist). Welchen Return-Code man im Fehlerfall zurückerhält, ist undefiniert. Mehr Informationen werden allerdings auch nicht benötigt, da man ja mit »XHInqTarget()« vorher gezielt diese Fähigkeit abtesten kann. »do_lock« gibt an, ob das Gerät verriegelt (1) oder entriegelt (0) werden soll.
Mit »XHStop()« wird ein Gerät gestoppt (geparkt) bzw. gestartet (entparkt). Welchen Code man im Fehlerfall zurückerhält, ist undefiniert. Mehr Informationen werden allerdings auch in diesem Fall nicht benötigt (siehe oben). »do_stop« gibt an, ob das Gerät gestoppt (1) oder wieder gestartet (0) werden soll. Bei etwaigen Zugriffen auf das gestoppte Gerät, sollte der Treiber selbst für das Wiederhochfahren sorgen.
Mit »XHEject()« wird das Medium ausgeworfen oder eingezogen. Welchen Code man im Fehlerfall zurückerhält, ist undefiniert (genau wie bei »XHLock()«. »do_eject« gibt an, ob das Medium ausgeworfen (1) oder eingezogen (0) werden soll.
»XHDrvMap()« liefert einen Bitvektor mit den über das XHDI-Protokoll unterstützten BIOS-Gerätenummern (wie etwa bei »Drvmap()«.
Mit »XHInqDev()« kann man Major Device Number, Minor Device Number, Startsektor und BPB eines BIOS-Geräts (im Gegensatz zu »Getbpb()« wird dadurch der Media-Change-Status des Geräts nicht zurückgesetzt) erfragen. Dabei wird ein Zeiger auf eine vom Aufrufer bereitgestellte BPBStruktur übergeben, die vom XHDI-Treiber gefüllt wird. Mögliche Return-Werte: »OK«, »EDRVNR« (Gerät kann z.Zt. nicht angesprochen werden, z.B. Medium nicht eingelegt), »EDRIVE« (falsche Gerätenummer) oder eine andere Fehlernummer. Bei »EDRVNR« darf man sich darauf verlassen, daß »major«, »minor« und »start_sector« korrekt zurückgeliefert werden.
Ein »start_sector« mit Wert $FFFFFFFF soll auf eine Partition hinweisen, die z.Zt. vom Treiber nicht bedient wird (z.B., wenn ein Wechselmedium mit »zu wenig« Partitionen eingelegt ist). Der zurückgelieferte BPB ist ungültig, wenn das Element »recsiz« Null ist. Ein Dateisystem ist durch Major- und Minor-Gerätenummer sowie Startsektor (mit der obigen Einschränkung) exakt spezifiziert. Über die Art des Dateisystems (FAT oder etwas anderes) ist damit nichts ausgesagt! Für »major«, »minor«, »start_sector« und »bpb« dürfen auch Nullzeiger übergeben werden.
»XHInqDriver()« liefert Informationen über den Treiber, der das angegebene Gerät bedient. »name«, »version« und »company« sind Zeiger auf Zeichenketten, in denen der Treibername (max. 17 Zeichen), die Versionsnummer (max. 7 Zeichen) und der Herstellername (max. 17 Zeichen) zurückgeliefert werden. »ahdi_version« gibt an, zu welcher AHDI-Version der Treiber kompatibel ist (siehe Versionsfeld in der PUN_INFO-Struktur). In »maxIPL« steht der höchste Interruptlevel, unter dem der Treiber arbeitsfähig ist (Normalwert für Treiber, die ihr Timing per "_hz_200« erledigen: 5). Für »name«, »version«, »company«, »ahdi_version« und »maxIPL« dürfen auch wieder Nullzeiger übergeben werden.
»XHNewCookie()« ist eine optionale Funktion, die auch mit der Fehlermeldung »EINVFN« beantwortet werden darf. Sie installiert einen zusätzlichen XHDI-Handler (Vorteil: der XHDI-Cookie zeigt nach wie vor auf dieselbe Adresse). Wer diese Funktion unterstützt muß also, falls dies der erste Aufruf dieser Art ist, anschließend so vorgehen, als hätte der XHDI-Cookie bei der Installation bereits auf »newcookie« gezeigt. Falls nicht: Funktion an nächsten Handler weiterleiten. Wer eine Mehrfachinstallation vornehmen möchte, sollte so vorgehen: zunächst testen, ob »XHNewCookie()« zum Erfolg führt; anderenfalls den Cookie »per Hand« versetzen.
»XHReadWrite()« schließlich ist das Pendant zur BIOS-Funktion »Rwabs()« zum Lesen bzw. Schreiben physikalischer Blocknummern. »rwflag« entspricht dem gleichen Parameter beim »Rwabs()«-Aufruf. Dabei werden allerdings nur die Bits 0..2 beachtet und Bit 3 wird ignoriert. Alle weiteren Bits sind reserviert und auf Null zu setzen.
Wie erwähnt, sollen sich auch mehrere Treiber den XHDI-Cookie teilen können. Dazu sind in den einzelnen Routinen einige Vorsichtsmaßnahmen notwendig, falls der XHDI-Cookie bereits vorher gesetzt war. Bei »XHGetVersion()« muß man zunächst durch den alten Vektor springen und dann das Minimum der dort erhaltenen und der eigenen Versionsnummer zurückliefern. »XHDrvmap()« sollte zunächst den alten Vektor durchspringen und anschließend die eigenen Drive-Bits hineinodern. Bei den anderen Funktionen: wenn es das eigene Gerät ist, normal fortfahren. Ansonsten: keinen Fehler melden, sondern durch den alten Vektor springen.
Soweit zur eigentlichen XHDI-Spezifikation. Die offizielle Fassung (deren Wortlaut im Zweifelsfall ausschlaggebend ist) finden Sie in der Datei »XHDI-100.ZOO«, die es in jeder gut sortierten Mailbox ([1]) geben sollte. Neben der Spezifikation enthält das Archiv auch eine Beispielimplementation des XHDI-Protokolls für die Diskettenlaufwerke, Beispielbindings für Pure C und eine Testapplikation, die XHDI-Funktionen benutzt (»df.ttp«).
Bereits jetzt wird XHDI von einigen Programmen unterstützt. Dazu gehören die neue Version der Virtual-Memory-Systeme »VRAM« (Alexander Herzlinger, Vertrieb: »OverScan GbR«) und »OUTSIDE« (Uwe Seimet, Vertrieb: » Maxon«), der Diskmonitor »Diskus« (Uwe Seimet, Vertrieb: »CCD«) mit dem dazugehörigen Festplattentreiber »HDDRIVER« oder das im Lieferumfang des Schnellkopierers »Kobold« befindliche »Check Open Files« (Hansi Richstein, Vertrieb: »Kaktus«). »SCSI-Tool« (Vertrieb: »Hard&Soft«) benutzt das XHDI-Protokoll in der aktuellen Version bereits zur Ermittlung von Systeminformationen. »HuSHI« (der dazugehörige Harddisktreiber) wird das XHDI-Protokoll ab Version 3.0 (noch nicht lieferbar) unterstützen.
Auf der CeBIT haben die meisten Festplattenhersteller signalisiert, daß die XHDI-Unterstützung für künftige Treiberversionen geplant sei. Damit scheint nun endlich ein flexibles, leistungsfähiges und vor allen Dingen ausbaubares Protokoll, das Chancen hat ein Standard zu werden, gefunden.
Quellennachweis:
[1] Julian Reschke u.a.: »XHDI-Spezifikation«, Version 1.00, 22. März 1992, erhältlich in der Maus MS 2 als »XHDI-100.ZOO«.
# | Typ | Name | Parameter |
0 | UWORD | XHGetVersion | (void); |
1 | LONG | XHInqTarget | (UWORD major, UWORD minor, ULONG *blocksize, ULONG *device_flags, char *product_name); |
2 | LONG | XHReserve | (UWORD major, UWORD minor, UWORD do_reserve, UWORD key); |
3 | LONG | XHLock | (UWORD major, UWORD minor, UWORD do_lock, UWORD key); |
4 | LONG | XHStop | (UWORD major, UWORD minor, UWORD do_stop, UWORD key); |
5 | LONG | XHEject | (UWORD major, UWORD minor, UWORD do_eject, UWORD key); |
6 | ULONG | XHDrvMap | (void); |
7 | LONG | XHInqDev | (UWORD bios_device, UWORD *major, UWORD *minor, ULONG *start_sector, BPB *bpb); |
8 | LONG | XHInqDriver | (UWORD bios_device, char *name, char *version, char *company, UWORD *ahdi_version, UWORD *maxIPL); |
9 | LONG | XHNewCookie | (ULONG newcookie); |
10 | LONG | XHReadWrite | (UWORD major, UWORD rninor, UWORD rwflag, ULONG recno, UWORD count, void *buf); |
Fehlernummer | Bedeutung |
0 | OK (OK) |
-1 | unspezifizierter Fehler (ERROR) |
-2 | Gerät nicht bereit (EDRVNR) |
-1 | ungültige Device/Taxgetnummer (EUNDEV) |
-32 | falsche Funktionsnummer (EINVFN) |
-36 | Gerät ist zur Zeit 'reserved' (BACCDN) |
-46 | BIOS-Device wird vom Treiber nicht bedient (EDRIVE) |
(-200 - N) | SCSI-Errorcode N (der 'Additional Sense Code' aus Byte 12 des 'Extended Sense Format') |