Vorstellung des Konzepts zum IowKit 2.0 API

Dies ist das deutsche Forum für alle Themen um den IO-Warrior. Beiträge bitte nur in Deutsch.

Moderator: Guido Körber

Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Vorstellung des Konzepts zum IowKit 2.0 API

Post by Robert Marquardt »

Das IowKit 2.0 API hat nun schon einige interne Beta-Revisionen hinter sich, aber wir haben uns entschlossen das Konzept nochmals zu überarbeiten.
An dieser Stelle will ich nun das Konzept ausführlich vorstellen und zur Diskussion stellen. Alle sind herzlich eingeladen Vorschläge und Kritik fast jeder Art hier abzugeben. Ich bitte allerdings um zielgerichtete Kritik.

Das 1.x API hat mehrere Einschränkungen und Schwächen. Das 2.0 API soll diese Probleme lösen, ohne dabei komplizierter zu werden. Als Zugabe bleibt das 1.x API erhalten.
Das ist natürlich ein hehres Ziel das man nur verfehlen kann, denn gerade die Handhabung des Ein- und Aussteckens von IO-Warriors wurde im 1.x API als zu kompliziert für den Gelegenheitsprogrammierer weggelassen.

Die Neuerungen:

1. dynamische Geräteverwaltung
Das Ein- und Ausstecken von IO-Warriors wird erkannt und gemeldet.
2. virtuelle Teilgeräte
Die IO-Warrior werden als Teilgeräte verwaltet. Eine Special Mode Function stellt sich als eigenes Gerät dar.
3. korrekte Ressource-Verwaltung
Ein Teilgerät wird von einem Programm belegt und ist dann für andere Programme gesperrt.
4. asynchrone Daten-Ereignisse
Die Key Matrix lässt sich mit dem 1.x API nicht richtig handhaben, da die Reports asynchrone Ereignisse darstellen, die zu beliebigem Zeitpunkt spontan eintreffen können.
5. einfachere Datenübergabe
Der reale Datenaustausch erfolgt über Reports. Das bisherige Konzept mit Puffern und Längen ist nicht einfach genug.
6. Erweiterbarkeit
Unterstützung zukünftiger neuer Mitglieder der IO-Warrior-Familie, die mehr und neue Funktionalität bereitstellen.

Die Einzelheiten dazu:

1.
Um das Ein- und Ausstecken der Geräte zu erkennen muss das API mit einer Callback-Funktion ausgerüstet werden. Es wird also bei der Initialisierungsfunktion des APIs eine Funktion übergeben, die jedesmal aufgerufen wird wenn ein IO-Warrior ein- oder ausgesteckt wird. Für bereits beim Aufruf der Initialisierungsfunktion eingesteckte IO-Warriors wird das Einstecken einfach simuliert.

2.
Ein IO-Warrior wird als mehrere virtuelle Teilgeräte verwaltet. Die IO-Pins und jede Special Mode Function wird als eigenes Gerät verwaltet. Dies erlaubt es einem Programm nur einen Teil des IO-Warriors zu benutzen und die anderen Teile bleiben für andere Programme verfügbar.
Das Einstecken eines IO-Warriors bewirkt nun das Eintreffen mehrerer Teilgeräte. Der Callback wird also mehrmals aufgerufen.

3.
Bisher konnte jedes Programm auf alle vorhandenen IO-Warrior zugreifen, ohne von den anderen Programmen zu wissen. Dabei konnte es natürlich zu Konflikten kommen.
Im 2.0 API muss man das Teilgerät zuerst belegen bevor es benutzt werden kann. Damit ist das Teilgerät den anderen Programmen entzogen. Die Belegung erfolgt im Callback. Dabei tritt eine gewisse Ungerechtigkeit auf. Ein freies Teilgerät wird den Programmen nacheinander vorgelegt. Wer also in dieser Daisy-Chain hinten steht dem kann das Teilgerät vor der Nase weggeschnappt werden. Dies ist allerdings nur mässig problematisch, da keine Strategie gerecht sein kann.
Zukünftige Programme sollten sich merken welchen IO-Warrior sie benutzen wollen. VID, PID und Seriennummer identifizieren einen IO-Warrior weltweit eindeutig. Da es nun bereits einige Produkte auf Basis von IO-Warriors gibt, wird es zunehmend wichtig das man nicht den ersten vorhandenen greift. Es ist sonst zu leicht möglich das man mit dem falschen IO-Warrior reden will.

4.
Die Special Mode Functions waren ursprünglich als Kommandointerface konzipiert, d. h. man schreibt ein Kommando an den IO-Warrior und erhält eine Antwort. Die Key-Matrix-Funktion hat dieses Konzept allerdings durchbrochen. Es kann jederzeit ein Report durch ein externes Ereignis (Tastendruck) ausgelöst werden.
Das 1.x API ist nicht für dieses Konzept geeignet. Das 2.0 API löst das Problem wieder durch einen Callback. Es gibt nun einen Daten-Callback, der für das virtuelle Teilgerät ausgelöst wird sobald ein Report eintrifft. Intern läuft dazu ein Lesethread. Im Daten-Callback kann man nun den Report direkt annehmen oder ihn zurücklegen lassen. Legt man den Report zurück, so wird er in eine Lesewarteschlange eingestellt und kann dann mit der normalen Read-Funktion des APIs abgeholt werden. Auf diese Weise kann man daher wahlweise weiterhin mit einfachen Write/Read-Paaren operieren oder die Reports per Callback verarbeiten.

5.
Die Daten werden im 2.0 API in einzelnen Reports gelesen und geschrieben. Es wird dabei ein Maximal-Report benutzt, d. h. der Report ist gross genug für jeden IO-Warrior. Das API kann selbst entscheiden wieviele Bytes des Reports gültig sind, da immer klar ist woher bzw wohin die Daten kommen und gehen.

6.
Ein Nebeneffekt der virtuellen Teilgeräte ist die elegante Handhabung älterer und neuerer Chiprevisionen. Nicht vorhandene Special Mode Functions werden einfach durch das Fehlen des entsprechenden Teilgerätes dargestellt. Neue Special Mode Functions ergeben ein weiteres Teilgerät.

Hier erst mal der Entwurf des 2.0 APIs als ganzes. Im folgenden werde ich dann noch die Einzelheiten durchsprechen. Ein paar Zeilen habe ich herausgekürzt, da sie nicht wichtig sind (oder zuviel verraten :).

Code: Select all

// values for action parameter of  IOWKIT2_DEVICECHANGE_EVENT 
#define IOWKIT2_ACTION_ARRIVAL 0
#define IOWKIT2_ACTION_REMOVAL 1

// max number of bytes in a report
#define IOWKIT2_REPORT_PAYLOAD_SIZE 63

// virtual IO-Warrior device kinds
#define IOWKIT2_KIND_HARDWARE      0
#define IOWKIT2_KIND_IO_PINS       1
#define IOWKIT2_KIND_IIC           2
#define IOWKIT2_KIND_LCD           3
#define IOWKIT2_KIND_SPI           4
#define IOWKIT2_KIND_INFRARED      5
#define IOWKIT2_KIND_LED_MATRIX    6
#define IOWKIT2_KIND_SWITCH_MATRIX 7

// Opaque IO-Warrior handle
typedef PVOID IOWKIT2_HANDLE;

// This is a max report which can hold any IO-Warrior report
typedef struct _IOWKIT2_REPORT
 {
  UCHAR ReportID;
  UCHAR Bytes[IOWKIT2_REPORT_PAYLOAD_SIZE];
 }
  IOWKIT2_REPORT, *PIOWKIT2_REPORT;

#define IOWKIT2_REPORT_SIZE sizeof(IOWKIT2_REPORT)

typedef struct _IOWKIT2_CONFIGINFO
 {
  ULONG Kind;
  ULONG Reason;
  IOWKIT2_REPORT Mask;
 }
  IOWKIT2_CONFIGINFO, *PIOWKIT2_CONFIGINFO;

typedef struct _IOWKIT2_HARDWAREINFO
 {
  // hardware info
  ULONG VendorID;
  ULONG ProductID;
  ULONG SerialNumber;
  ULONG Revision;
  ULONG InputReportByteLength;
  ULONG OutputReportByteLength;
  BOOL IsConnected;  // the hardware is still connected
 }
  IOWKIT2_HARDWAREINFO, *PIOWKIT2_HARDWAREINFO;

typedef struct _IOWKIT2_DEVICESTATE
 {
  // virtual device info
  ULONG Kind;        // one of the IOWKIT2_KIND_* values
  BOOL IsActive;     // the special mode is switched on
 }
  IOWKIT2_DEVICESTATE, *PIOWKIT2_DEVICESTATE;

typedef struct _IOWKIT2_DEVICEPARAMETERS
 {
  // device parameters changeable through IowKit2SetDeviceParameters
  BOOL IsUsed;                   // the device is in use by the calling application
  IOWKIT2_REPORT Mask;           // used to reserve specific IO-Pins
  ULONG PendingReports;          // number of reports waiting in the input queue
  ULONG ReadTimeout;             // timeout for IowKit2Read
  ULONG WriteTimeout;            // timeout for IowKit2Write
  IOWKIT2_DATA_EVENT DataEvent;  // callback for incoming reports
  PVOID Context;                 // free for any use
 }
  IOWKIT2_DEVICEPARAMETERS, *PIOWKIT2_DEVICEPARAMETERS;
  
typedef struct _IOWKIT2_DEVICEINFO
 {
  IOWKIT2_HARDWAREINFO Hardware;
  IOWKIT2_DEVICESTATE State;
  IOWKIT2_DEVICEPARAMETERS Parameters;
 }
  IOWKIT2_DEVICEINFO, *PIOWKIT2_DEVICEINFO;

typedef BOOL (IOWKIT2_API *IOWKIT2_DATA_EVENT)(IOWKIT2_HANDLE devHandle, PIOWKIT2_REPORT report, ULONG size);
typedef void (IOWKIT2_API *IOWKIT2_DEVICECHANGE_EVENT)(ULONG action, IOWKIT2_HANDLE devHandle, PVOID Context);
typedef void (IOWKIT2_API *IOWKIT2_CONFIGCHANGE_EVENT)(IOWKIT2_HANDLE devHandle, PIOWKIT2_CONFIGINFO oldConfig, PIOWKIT2_CONFIGINFO newConfig);

DWORD IOWKIT2_API IowKit2Initialize(IOWKIT2_DEVICECHANGE_EVENT DevChangeEvent, IOWKIT2_CONFIGCHANGE_EVENT ConfigChangeEvent, PVOID Context);
void IOWKIT2_API IowKit2Finalize(void);
void IOWKIT2_API IowKit2Enumerate(void);
ULONG IOWKIT2_API IowKit2GetNumDevs(ULONG Kind, BOOL Logical);

int IOWKIT2_API IowKit2Write(IOWKIT2_HANDLE devHandle, PIOWKIT2_REPORT report);
int IOWKIT2_API IowKit2Read(IOWKIT2_HANDLE devHandle, PIOWKIT2_REPORT report);
int IOWKIT2_API IowKit2ReadNonBlocking(IOWKIT2_HANDLE devHandle, PIOWKIT2_REPORT report);

int IOWKIT2_API IowKit2ReadImmediate(IOWKIT2_HANDLE devHandle, PIOWKIT2_REPORT report);

BOOL IOWKIT2_API IowKit2GetDeviceInfo(IOWKIT2_HANDLE devHandle, PIOWKIT2_DEVICE devInfo);
BOOL IOWKIT2_API IowKit2SetDeviceParameters(IOWKIT2_HANDLE devHandle, PIOWKIT2_DEVICEINFO devInfo);

PCSTR IOWKIT2_API IowKit2Version(void);
Die API-Funktionen sind in mehrere Gruppen unterteilt. Zuerst kommen die allgemeinen Verwaltungsfunktionen.

Mit IowKit2Initialize() meldet man sich zur Benutzung von IO-Warriors an. Es wird ein IOWKIT2_DEVICECHANGE_EVENT übergeben, der dann prompt aufgerufen wird sofern freie IO-Warriors vorhanden sind. Das bezieht sich natürlich auf virtuelle Teilgeräte. Mit IowKit2Finalize() meldet man sich wieder ab. Alle benutzten Teilgeräte werden wieder freigegeben und entsprechend bekommen andere Programme per Callback die nun wieder vorhandenen Teilgeräte gemeldet.
Mit IowKit2Enumerate() kann man jederzeit sich alle freien Geräte per Callback melden lassen. Es ist nämlich nicht möglich ausserhalb des Callbacks ein Teilgerät zu belegen.
IowKit2GetNumDevs() meldet die Anzahl vorhandener Geräte oder Teilgeräte. Mit dem Parameter Kind kann man angeben welche Teilgeräte bzw Geräte gezählt werden sollen. IOWKIT2_KIND_HARDWARE zählt alle echten IO-Warriors egal von welcher Art. Mit der ProductID als Parameterwert kan man die echten IO-Warrior nach Art zählen, also beispielsweise nur alle vorhandenen IO-Warrior 40. Die anderen IOWKIT2_KIND_ Konstanten zählen virtuelle Teilgeräte. Für sie gibt der Parameter Logical an ob alle vorhandenen oder nur die vom eigenen Programm belegten plus die noch freien Teilgeräte gezählt werden.

Die nächste Gruppe von Funktionen betrifft das Lesen und Schreiben. Dazu ist zu sagen das beim Schreiben abgesichert wird das nur für das virtuelle Teilgerät erlaubte ReportIDs geschrieben werden. Beim Lesen werden entsprechend nur die für das Teilgerät gedachten Reports geliefert. IowKit2ReadNonBlocking() liest nicht-blockierend, d. h. wenn kein Report wartet, dann kommt die Funktion ohne Report sofort zurück.
Als wichtigste Besonderheit wird das IowKit2Write() überwacht, damit das Ein- und Ausschalten der Special Mode Function erkannt werden kann. Da einige Special Mode nicht gleichzeitig aktiv sein können, muss dann von einem zum anderen Teilgerät eine Konfigurationsänderung gemeldet werden. Der zugehörige IOWKIT2_CONFIGCHANGE_EVENT wird bei IowKit2Initialize() mit angegeben. Dieser Teil des APIs ist noch nicht ganz ausdefiniert. Bei den Special-Mode-Teilgeräten ist es nur die Nachricht das das Teilgerät nicht mehr aktiviert werden kann. Ein IowKit2Write() zum Einschalten des Special Mode wird also scheitern.
Die Parameter des IOWKIT2_CONFIGCHANGE_EVENT und die Struktur IOWKIT2_CONFIGINFO sind noch nicht in Stein gemeisselt. Ich nehme hier gerne Vorschläge für eine elegante Definition entgegen.
Falls sich jemand wundert warum es keine eigenständige Funktion zum Ein- und Ausschalten des Special Mode gibt, so ist die Antwort das damit die Flexibilität verloren geht. Der zugehörige Report kann noch mehrere Konfigurationsbytes enthalten. Da ist es doch einfacher und eleganter das Schreiben zu überwachen, da das sowieso erfolgen muss.

IowKit2ReadImmediate() bildet wohl eine eigene Gruppe. Es wird die Special Mode Function "Get Current Pin Status" abgebildet. Diese bildet kein eigenes virtuelles Teilgerät. Stattdessen kann man IowKit2ReadImmediate() nur auf das IO-Pin-Teilgerät aufrufen. Eine elegantere Möglichkeit diese Funktionalitat einzugliedern ist mir nicht eingefallen.

Die dritte Gruppe wird von den Informationsfunktionen gebildet.
IowKit2GetDeviceInfo() liefert alle Infos zum virtuellen Teilgerät und zum zugehörigen realen IO-Warrior. Diese Funktion wird logischerweise zuerst im IOWKIT2_DEVICECHANGE_EVENT aufgerufen, um herauszubekommen mit welchem Gerät man es zu tun hat. Ich habe davon abgesehen einen PIOWKIT2_DEVICEINFO-Parameter am IOWKIT2_DEVICECHANGE_EVENT-Callback vorzusehen. Es soll immer klar sein das die Daten ein Schnappschuss des aktuellen Gerätezustands sind.
Insbesondere trifft dies auf die IOWKIT2_DEVICEPARAMETERS-Teilstruktur zu. Diese wird nämlich auch zur Änderung der Parameter verwendet. Mit IowKit2SetDeviceParameters() kann man jederzeit Änderungen in der IOWKIT2_DEVICEPARAMETERS-Teilstruktur an das Gerät schreiben. Auf diese Weise ist ein Sortiment von API-Funktionen elegant auf eine reduziert worden.

IsUsed ist der wichtigste der Parameter. Mit dem Setzen dieses Parameters wird das Teilgerät in Benutzung genommen. Es ist nun belegt und kein anderes Programm kann es benutzen bis IsUsed wieder zurückgesetzt wird. Das Teilgerät bleibt sogar vorhanden wenn man den zugehörigen IO-Warrior aussteckt. Es wird dann zum Zombie-Gerät und hört auf zu funktionieren. IowKit2Write() wird immer versagen. IowKit2Read() und IowKit2ReadNonBlocking() versagen sobald keine gepufferten Reports mehr vorhanden sind. Die normale Vorgehensweise ist aber das man das Teilgerät im IOWKIT2_DEVICECHANGE_EVENT für das Ausstecken freigibt.

Mask ist nur für das IO-Pins-Teilgerät von Bedeutung. Man setzt in den Bytes des Reprots die Bits der IO-Pins, die man reservieren will. Die ReportID des Reports wird dazu missbraucht anzugeben ob die Makse gültig ist. Reserviert man Pins, die zu Special-Mode-Teilgeräten gehören, so werden diese unbenutzbar und das Teilgerät taucht nicht mehr auf.
Sind nicht alle IO-Pins reserviert, so kann ein anderes Programm sie belegen. Es werden also intern mehrere virtuelle Io-Pins-Teilgeräte verwaltet.
Dieser Teil des APIs (besonders im Zusammenspiel mit dem IOWKIT2_CONFIGCHANGE_EVENT) muss noch genauer durchgesprochen werden.

PendingReports meldet wieviele Reports gerade zum Lesen in der Warteschlange stehen. Setzt man hier Null ein, so werden alle Reports weggeworfen. Dies implementiert also einen Flush der Warteschlange. Andere Werte als Null werden ignoriert, da es sonst zu ungewollten Reportverlusten kommen könnte.

Zu ReadTimeout und WriteTimeout muss wohl nicht viel erklärt werden.

DataEvent ist ein weiterer Callback. Bevor ein Report in die Warteschlange eingefügt wird, wird er per IOWKIT2_DATA_EVENT-Callback an die Applikation gemeldet. Der Rückgabewert des Callbacks bestimmt ob der Report in die Warteschlange gelangt oder ob er bereits im Callback verarbeitet wurde. Mit diesem Mechanismus kann man die Reports in einer eigenen Funktion verarbeiten, nur einige Reports ausfiltern oder klassisch mit IowKit2Write/IowKit2Read-Paaren arbeiten.

Context kann beliebig belegt werden. Der Parameter Context von IowKit2Initialize() ist nicht mit diesem Element identisch. Der Parameter von IowKit2Initialize() wird an IOWKIT2_DEVICECHANGE_EVENT weitergereicht. Er lässt sich in der Applikation sehr gut für die Rückkehr in die Objektorientierung verwenden. "this" in C++ bzw. "Self" in Delphi ist ein Kandidat für den Parameter.


Jetzt möchte ich nochmals zu reger Diskussion aufrufen. Nichts ist in Stein gemeisselt. Wem ein besserer Ansatz einfällt, der wird gehört werden. Auch das Besserwissen an der mickrigsten Kleinigkeit wird gerne akzeptiert.
Die Implementation dieses Ansatzes ist schwierig genug. Es wird wohl auch unter Windows auf einen Daemon hinauslaufen, d. h. die DLL wird eine unsichtbare Applikation starten, die das Lesen und Verwalten der realen Geräte für die DLL übernimmt.
Benedikt.Seidl
Posts: 19
Joined: Sat Aug 14, 2004 10:45 am
Location: thalmässing -> bayern

Post by Benedikt.Seidl »

hallo

vom dau mal wieder eine dau frage:

das ganze ist nur eine neue software und läuft mit meinem "alten" warrior?

ich nehme nicht an, dass es auch eine version für runtime revolution in form eines c externals geben wird?

dann muss ich mich wohl selber dran versuchen müssen (muahah)

SEIDL.
Guido Körber
Site Admin
Posts: 2876
Joined: Tue Nov 25, 2003 10:25 pm
Location: Germany/Berlin
Contact:

Post by Guido Körber »

Ja, natürlich ist das nur eine neue Software die mit den existierenden IO-Warrior und dann auch irgendwann mit zukünftigen Chips laufen wird.

Was die Verwendbarkeit von verschiedenen Programmierumgebungen betrifft, so wird es dabei bleiben wie es bisher ist: Es gibt die DLL, die prinzipiell von überall aus benutzt werden kann. Spezifische Wrapper wird es für ein paar Dinge (z.B. Java) fertig geben, andere sind wie bisher selber zu schaffen.
wayoda
Posts: 362
Joined: Fri Dec 19, 2003 12:00 pm
Location: Wuppertal/Germany

Post by wayoda »

(Das ist aber jetzt ganz schön lang geworden )

Ich glaube, der Ansatz mit den vielen Teilgeräten ist mir zu kompliziert, aber ich fange lieber erst mal mit den positiven Aspekten an:

Die Möglichkeit einen IOWarrior komplett zu konfigurieren gefällt mir sehr gut. Insbesondere, dass eine Änderung an der Konfiguration an einem Teilgeräte per callback auch den Besitzern der anderen Teilgeräte angezeigt wird scheint vernünftig.

Aber ich würde sagen, dass sind mir eindeutig zu viele Teilgeräte. Mir würden eigentlich schon 2 Geräte reichen:
Die IO-Pins und die SpecialModes.
Ein IOWarrior24 kommt bei eurem Vorschlag schon als 6 Teilgeräte an und wenn bei der Maskierung der IO-Pins noch welche frei bleiben, können es sogar noch mehr werden.

Jede Applikation muß doch dann entscheiden :
Brauche ich dieses Teilgerät?
Wenn es ein Teilgerät für die IO-Pins ist muß man auch noch testen ob die gewünschten Ports/Bits zur Verfügung stehen?

Ich kann mir schon Vorstellen, dass es Umstände gibt bei denen Programm1 nur was mit den IO-Pins anfangen will und Programm2 nur die SpecialModes verwendet. Aber 5 verschiedene Programme zu einzusetzen, die für jeweils verschiedene SpecialModes zuständig sind sieht mir eher nach einem Designfehler aus.
Auch die Aufgabe in einer Objektorientierten Programmiersprache eine passende Klassenstruktur zu erstellen zu müssen läßt bei mir schon einen
leichten Anflug von Kopfschmerzen entstehen ;-)

Ich mache einfach mal einen Alternativvorschlag:

Ein IOWarrior besteht aus 2 Teilgeräten, den IO-Pins und den SpecialModes.

Jedes dieser Teilgeräte kann konfiguriert werden:
Für die IO-Pins würde ich dabei nicht nur eine allgemeine Maske für die benutzten Ports verwenden, sondern sogar eine Maske für die Eingänge und eine weitere für die Ausgänge. Alle Pins die in der Eingangs-Maske enthalten könnte man beim Schreiben auf die IO-Pins library-intern ausmaskieren, damit sie nicht versehentlich auf Low gesetzt werden. Alle Pins die nicht in der Eingangs- und der Ausgangsmaske enthalten sind, stehen für die SpecialModes zur Verfügung.

Für die SpecialModes gibt es ebenfalls eine Maske, die festlegt welche SpecialModes aktivierbar sein sollen. Ob man diese dann tatsächlich benutzt bleibt der Applikation überlassen.

Die beiden Konfigurationen werden innerhalb der Library auf Zulässigkeit geprüft. Hat man z.B. am IO-Pin Gerät eines IOWarrior24 Port 0.0 als Eingang maskiert, führt der Versuch den SpecialMode RC5 zu belegen zu einem Fehler.
Hat man umgekehrt am SpecialMode-Teilgerät den LCD-Mode belegt, kann man am IO-Pin-Gerät nicht mehr die entsprechenden Pins konfigurieren.

Die Datenstruktur CONFIGINFO sähe bei mir etwa so aus:

Code: Select all

typedef struct _IOWKIT2_CONFIGINFO { 
    IOWKIT2_REPORT InputMask; //Eingänge
    IOWKIT2_REPORT OutputMask; //Ausgänge
    //Die Pins die durch SpecialModes verwendet werden
    IOWKIT2_REPORT SpecialModeMask; 
    //Alles was nicht oben abgedeckt wurde
    IOWKIT2_REPORT DontCareMask; 
} 
IOWKIT2_CONFIGINFO, *PIOWKIT2_CONFIGINFO; 
Und nun ein kleiner Rundgang durch den Rest der IowKit2:
Die Callbacks bleiben so wie vorgeschlagen. Ein CONFIG_CHANGE_EVENT wird an beide TeilGeräte geliefert, damit jeder weiß wo er steht.

Initialize(...) kann so bleiben

Finalize(...) kann so bleiben

Enumerate(...) kann so bleiben

GetNumDevs(...) kann so bleiben

Write(...)
An den IO-Pins werden alle Bits ausmaskiert, die in der Eingangs-Maske gesetzt sind, und es wird eine Warnung zurückgegeben.
An den SpecialModes wird ein Fehler gemeldet, wenn der SpecialMode nicht vorher konfiguriert wurde. In diesem Fall werden die Daten nicht geschrieben.
Read(...)
diese Funktion kann zusammen mit dem entsprechenden ReadTimeout entfallen. Es gibt eigentlich kein gutes Argument überhaupt eine blockierende Funktion in der Library anzubieten. Verwendet der
Applikationsentwickler sowieso einen extra Thread um Daten vom Gerät zu lesen, dann kann er auch ReadNonBlocking() benutzen.Verwendet er nur einen Thread für die ganze Applikation, dann wäre es fatal hier einige Sekunden zu warten, und damit alle Events zu blockieren.

ReadNonBlocking(...) wird umbenannt zu Read(...) s.o.

ReadImmediate(...)
kann so bleiben, (oder wird umbenannt zu ReadCurrentIOStatus(...) damit klarer wird was hier passiert). Übrigens sollte man die Funktion doch für alle Teilgeräte anbieten (nicht nur an den IO-Pins). Es ist ja eine reine
Lese-Operation.

GetDeviceInfo(...) kann als Lese-Operation für die Hardware/Konfiguration des IOWarrior so bleiben.

SetDeviceInfo(...) gefällt mir gar nicht :-(
IsUsed ist der wichtigste der Parameter. Mit dem Setzen dieses Parameters wird das Teilgerät in Benutzung genommen. Es ist nun belegt und kein anderes Programm kann es benutzen bis IsUsed wieder zurückgesetzt wird.
Diese Vorgehensweise um ein Gerät zu verwenden ist mir zu versteckt!
Ich wäre da eher für die Verwendung der traditionellen Funktionsnamen:

Code: Select all

int IOWKIT2_API IowKit2Open(IOWKIT2_HANDLE
devHandle,PIOWKIT2_DEVICEINFO devInfo); 
BOOL IOWKIT2_API IowKit2SetDeviceConfig(IOWKIT2_HANDLE devHandle,
PIOWKIT2_DEVICEINFO devInfo); 
int IOWKIT2_API IowKit2Close(IOWKIT2_HANDLE);
IowKit2Open
belegt das Teilgerät für meine Applikation und setzt in dem Parameter devInfo die Callbacks und die Konfiguration des Gerätes. Der Rückgabewert ist -1 falls der Handle ungültig ist, oder die gewünschte Konfiguration nicht zur Verfügung steht (Widerspruch zu einer bestehenden Konfiguration am anderen Teilgerät).
Rückgabewert 0 Falls das Gerät dann irgendwie doch nicht belegt werden kann (hoppla, gerade den Stecker gezogen wo ich es verwenden wollte)
Rückgabewert 1 falls das Gerät nun zur Verfügung steht.

IowKit2SetDeviceConfig
Verwendet man nur um eine bestehende Konfiguration zu Ändern.
Rückgabewert auch hier ein int. (-1=Ungültiger Handle, 0=Änderung nicht möglich das sie der Konfiguration des anderen Teilgerätes widerspricht, 1=Ok geändert)

IowKit2Close(IOWKIT2_HANDLE)
Gibt das Teilgerät wieder frei.

Open/Close und Set-/GetDeviceConfig versteht jeder sofort ohne Nachdenken. Diese Funktionalität in einem struct IOWKIT2_DEVICEPARAMETERS zu verstecken finde ich nicht einleuchtend. Vielleicht sollte man einfach mal die Dokumentation zu beiden Varianten schreiben um zu Prüfen bei welchem Ansatz man weniger Zeilen braucht.

Das Gleiche gilt für die PendingReports (=Anzahl der ungelesenen Reports im Puffer). Zwei Funktionen:

Code: Select all

int IOWKIT2_API getPendingReports(IOWKIT2_HANDLE);
int IOWKIT2_API clearPendingReports(IOWKIT2_HANDLE);
Sind bei der Doku jeweils in 2 Zeilen abgehandelt. Wer es nicht braucht muß sich nie Gedanken darüber machen und nie die 20 Zeilen in der Dokumentation zu IOWKIT2_DEVICE_PARAMETERS machen.
Ein wenig Code gefällig?...

Code: Select all

IOWKIT2_DEVICE_INFO info;
memset(&info,0,sizeof(IOWKIT2_DEVICE_INFO);
//jetzt alles in einstellen , aber leider info.Parameters.PendingReports
vergessen ?
IowKit2_setDeviceDeviceParameters(handle, &info);
//Hoppla, jetzt hab ich doch aus versehen alle Daten im Puffer gelöscht :-(
Zum Schluss noch eine Funktion, die ich auch gerne sehen würde:

Code: Select all

/*
  Tests wether the device is still plugged in.
  Returns -1 for an invalid handle, 0 if the device is unplgged, 
  1 if the device is still available
*/
int IowKit2_isConnected(IOWKIT2_HANDLE handle);
Grüße Eberhard
HappyAura
Posts: 68
Joined: Sun Feb 27, 2005 2:13 pm

Post by HappyAura »

moinmoin, dann will ich auch mal ein wenig meinen senf zur api gestaltung dazugeben.
robert wrote: IsUsed ist der wichtigste der Parameter. [...] Das Teilgerät bleibt sogar vorhanden wenn man den zugehörigen IO-Warrior aussteckt. Es wird dann zum Zombie-Gerät und hört auf zu funktionieren.
Das ist eine eigenschaft, die mir nicht wirklich zusagt. Ich denke, der IOW wird auch von vielen Hobbyprogrammierern benutzt oder von leuten, die mit der programmierung eher wenig erfahrung haben. da läuft das coden gerne mal nach dem prinzip "Trial and error", wenn was nicht funktioniert hat, den IOW durch ein rausziehen und erneutes reistecken resetten. das lesen einer Doku wird ja im allgemeinen eher vernachlässigt, so dass ich vermute, dass durchaus einige leute geben wird, die da ein wenig ins grübeln kommen, weshalb änderungen nicht funktionieren oder ähnliches. wenn unter windows sowieso ein deamon läuft, ist es vielleicht möglich bei einem unplug/plug die teilgeräte frei zu geben.
aber ich denke, das wird man sonst schon noch selbst hinbekommen,muß man ja letztendlich schon, geht nur um die entwicklungsphase von programmen.
wayoda wrote:Ich kann mir schon Vorstellen, dass es Umstände gibt bei denen Programm1 nur was mit den IO-Pins anfangen will und Programm2 nur die SpecialModes verwendet. Aber 5 verschiedene Programme zu einzusetzen, die für jeweils verschiedene SpecialModes zuständig sind sieht mir eher nach einem Designfehler aus.
für jeden special mode ein eigenes programm ist gar nicht so seltend. ich geh auch wieder vom hobbyanwender aus. mir ging es anfangs nur um eine Fernbedienung für den PC. dann hab ich so geschaut, was der IOW noch so kann, dann kam das display dazu und mittlerweile spiele ich noch mit I2C gelegentlich ein wenig rum. die anwendungen haben inhaltlich nix mit einander zu tun, in sofern macht es schon sinn, das über einzelnde geräte zu realisieren.

mit callbacks hab ich noch keine erfahrung, aber ich denke, dass man sich die neue api sicher schnell durch die doku und beispielprogramme beipulen kann.

da hat man sich 'nen display an seinen pc gebastelt und fragt sich irgendwann, was der IOW denn noch so kann, also spielt man mal mit I2C, oder ähnlichem rum.

an sonsten machen die ansätze auf mich als laien erst einmal einen guten eindruck.

gruß Martin
Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Post by Robert Marquardt »

wayoda wrote: Aber ich würde sagen, dass sind mir eindeutig zu viele Teilgeräte. Mir würden eigentlich schon 2 Geräte reichen:
Die IO-Pins und die SpecialModes.
Ein IOWarrior24 kommt bei eurem Vorschlag schon als 6 Teilgeräte an und wenn bei der Maskierung der IO-Pins noch welche frei bleiben, können es sogar noch mehr werden.
Genau das geht nicht. Es gibt zu viele Faelle bei denen zwei special Modes von unterschiedlichen Programmen benutzt werden wollen. IR und LCD sind da zu nennen.
wayoda wrote: Jede Applikation muß doch dann entscheiden :
Brauche ich dieses Teilgerät?
Wenn es ein Teilgerät für die IO-Pins ist muß man auch noch testen ob die gewünschten Ports/Bits zur Verfügung stehen?

Ich kann mir schon Vorstellen, dass es Umstände gibt bei denen Programm1 nur was mit den IO-Pins anfangen will und Programm2 nur die SpecialModes verwendet. Aber 5 verschiedene Programme zu einzusetzen, die für jeweils verschiedene SpecialModes zuständig sind sieht mir eher nach einem Designfehler aus.
Da gibt es mehrere Konfigurationen die Sinn machen. Ein Programm das LCD benutzt und einige IO-Pins fuer einige Extra-Leitungen des LCD (Umschaltung bei Dual-LCD-Controllern) waere da zu nennen.
wayoda wrote: Auch die Aufgabe in einer Objektorientierten Programmiersprache eine passende Klassenstruktur zu erstellen zu müssen läßt bei mir schon einen
leichten Anflug von Kopfschmerzen entstehen ;-)
Garnicht so schlimm. Man muss sich nur auf das Teilgeraet-Konzept einlassen, dann sieht es genauso aus wie vorher. Entweder macht man eine Typisierung oder abgeleitete Klassen.
Das ist immer so. Je mehr Aufwand man ins Design steckt, desto leistungsfaehiger und flexibler wird es dann in der Benutzung.
"Leider" koennen wir es hier nicht wie Microsoft machen. Dort wird ein API solange verkrueppelt bis es auch der letzte Depp versteht. Dabei geht aber die Leistungsfaehigkeit des APIs verloren.
wayoda wrote: Jedes dieser Teilgeräte kann konfiguriert werden:
Für die IO-Pins würde ich dabei nicht nur eine allgemeine Maske für die benutzten Ports verwenden, sondern sogar eine Maske für die Eingänge und eine weitere für die Ausgänge. Alle Pins die in der Eingangs-Maske enthalten könnte man beim Schreiben auf die IO-Pins library-intern ausmaskieren, damit sie nicht versehentlich auf Low gesetzt werden. Alle Pins die nicht in der Eingangs- und der Ausgangsmaske enthalten sind, stehen für die SpecialModes zur Verfügung.
Das ist auf jeden Fall eine gute Idee die uebernommen wird. Zusaetzliche Sicherheit im API kann nie schaden.
wayoda wrote: Read(...)
diese Funktion kann zusammen mit dem entsprechenden ReadTimeout entfallen. Es gibt eigentlich kein gutes Argument überhaupt eine blockierende Funktion in der Library anzubieten. Verwendet der
Applikationsentwickler sowieso einen extra Thread um Daten vom Gerät zu lesen, dann kann er auch ReadNonBlocking() benutzen.Verwendet er nur einen Thread für die ganze Applikation, dann wäre es fatal hier einige Sekunden zu warten, und damit alle Events zu blockieren.
Das ist so nicht richtig. Die meisten Special Modes funktionieren im Befehl/Antwort-Modus. Man schreibt einen Befehl und liest die Antwort. Da muss man wirklich warten bis die Antwort da ist und das kann einige Millisekunden dauern. Man braucht also ein blockierendes Read. Ein nicht blockierendes Read braucht man fuer die primitiven Programmiersprachen. Wir haben schon Experimente mit VBA gesehen!
wayoda wrote: ReadImmediate(...)
kann so bleiben, (oder wird umbenannt zu ReadCurrentIOStatus(...) damit klarer wird was hier passiert). Übrigens sollte man die Funktion doch für alle Teilgeräte anbieten (nicht nur an den IO-Pins). Es ist ja eine reine
Lese-Operation.
Die Namensaenderung ist gut. Ich werde sie Montag einarbeiten.
Fuer die Special Mode Teilgeraete macht sie aber keinen Sinn. Da ist es logischer zu erlauben das IO-Pins-Teilgeraet ohne Pinbelegung zu greifen und darauf ReadCurrentIOStatus durchzufuehren.
Das virtuelle IO-Pins-Teilgeraet gibt es uebrigens fuer jedes benutzende Programm nur einmal. Das es intern anders ist bleibt unsichtbar.
wayoda wrote: SetDeviceInfo(...) gefällt mir gar nicht :-(

Diese Vorgehensweise um ein Gerät zu verwenden ist mir zu versteckt!
Ich wäre da eher für die Verwendung der traditionellen Funktionsnamen:
Gutes Argument. Da habe ich bei der Verschlankung des APIs uebertrieben. "Es ist aber doch so elegant." ;-)

Bei den Device-Parametern moechte ich eigentlich weitgehend bleiben.
Der Vorteil liegt hier darin das Erweiterungen nicht zu neuen API-Funktionen fuehren. Wenn man noch ein Versions-Element in der Struktur hinzufuegt, dann kann man auch noch mit zukuenftigen API-Versionen kompatibel bleiben. Microsoft macht das ja mit einer Laengenangabe in einigen Strukturen.

Aber wie gesagt das muss man noch ein bischen hin und her diskutieren.
HappyAura
Posts: 68
Joined: Sun Feb 27, 2005 2:13 pm

Post by HappyAura »

Hallo Robert,

entschuldige die frage, aber wann ist mit eine fertigstellung der neuen Api zu rechnen? ist da schon ein termin abzusehen? ich meine dunkel irgendwas von mitte märz bis anfang april zu erinnern, weiß aber nicht, ob diese info aus der zeit vor der umstellung stammt oder vielleicht sogar komplett meinen gehirnwindungen entsprungen ist.
ich soll für jemand ein projekt programmieren und bin gerade am überlegen, ob ich auf die neue api warten sollte oder nicht.

besten Dank,
Martin
Guido Körber
Site Admin
Posts: 2876
Joined: Tue Nov 25, 2003 10:25 pm
Location: Germany/Berlin
Contact:

Post by Guido Körber »

Also der Termin stammt noch aus der Zeit vor den letzten Designänderungen. Ich denke mal April ist halbwegs realistisch, aber das ist noch keine feste Zusage.
Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Post by Robert Marquardt »

Das scheint mir auch realistisch. Ich ueberarbeite heute diesen Thread, damit ich die vorgeschlagenen Aenderungen sorgfaeltig durchdenken kann.
Im Augenblick sehe ich keine unueberwindlichen Hindernisse. Nur die weitere Unterstuetzung des 1.4 APIs koennte schwierig werden. Na schlimmstenfalls werden die Geraete dann intern komplett getrennt verwaltet.
Guido Körber
Site Admin
Posts: 2876
Joined: Tue Nov 25, 2003 10:25 pm
Location: Germany/Berlin
Contact:

Post by Guido Körber »

Robert Marquardt wrote:
wayoda wrote: Jedes dieser Teilgeräte kann konfiguriert werden:
Für die IO-Pins würde ich dabei nicht nur eine allgemeine Maske für die benutzten Ports verwenden, sondern sogar eine Maske für die Eingänge und eine weitere für die Ausgänge. Alle Pins die in der Eingangs-Maske enthalten könnte man beim Schreiben auf die IO-Pins library-intern ausmaskieren, damit sie nicht versehentlich auf Low gesetzt werden. Alle Pins die nicht in der Eingangs- und der Ausgangsmaske enthalten sind, stehen für die SpecialModes zur Verfügung.
Das ist auf jeden Fall eine gute Idee die uebernommen wird. Zusaetzliche Sicherheit im API kann nie schaden.
Negativ. Das wird auf keinen Fall übernommen, es gibt auch die Situation, dass ein Pin sowohl als Eingang als auch als Ausgang genutzt wird. Hier eine Maskierung zu machen verkompliziert die Sache nur unnötig.
wayoda
Posts: 362
Joined: Fri Dec 19, 2003 12:00 pm
Location: Wuppertal/Germany

Post by wayoda »

Vielleicht noch eine letzte Bitte:

Könnte man die Funktion GetNumDevs noch um die Seriennummer eines IOWarrior erweitern?
Begründung:
Nehmen wir mal an ich habe ein Programm geschrieben, das auf den IOWarrior24 mit der Seriennummer 0x1000 wartet. Leider war ein anders IowKit-Lib Programm schneller, bzw. vor mir in der Callback-Kette der DEVICE_CHANGE_EVENTS und hat sich das ganze Gerät schon unter den Nagel gerissen. Wenn ich das richtig verstanden habe, bekomme ich davon aber gar nichts mit? Ich habe also nicht die Möglichkeit zu unterscheiden ob mein Gerät gar nicht eingesteckt ist, oder ob ein anderes Programm einfach vor mir alle Teilgeräte belegt hat.
Als letzten Seufzer könnte mein Programm dann dem User zumindest noch sagen:
Ich weiß, dass Du mein Gerät eingesteckt hast. Leider läuft auf deinem Rechner aber noch ein anderes Programm, was verhindert, dass Du die Hardware die ich dir verkauft habe auch benutzen kannst!

Code: Select all

/**
 * Count the devices
 * @param Kind provides the ProductId of the device
 * @param Serial if you provide a value!=0 returns wether the IOWarrior 
 * is actually plugged in, even though it hasn't been reported in the
 * Callback
 * @param Logical as dokumented previously
 */
ULONG IOWKIT2_API IowKit2GetNumDevs(ULONG Kind, ULONG Serial, BOOL Logical); 
Eberhard
Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Post by Robert Marquardt »

Laesst sich machen. Da koennte es nur Probleme mit der Seriennummer 0 geben, da das auf alte IO-Warrior ohne Seriennummer triggert. Ich denke aber mit einem $FFFFFFFF als Parameter laesst sich das erschlagen.
Die Seriennummern werden beim Programmieren der Chips einfach aufwaerts gezaehlt und es duerfte daher kein Problem sein diese Seriennummer zu reservieren :-)
$FFFFFFFF heisst dann die Seriennummer nicht beachten beim Zaehlen.

Die Funktion kann ja auch noch geaendert werden. Es wird jetzt die Funktionalitaet und die Strukturierung festgeklopft. Hier und da ein bischen an den Parametern zu drehen ist ja auch spaeter kein Problem.
Guido Körber
Site Admin
Posts: 2876
Joined: Tue Nov 25, 2003 10:25 pm
Location: Germany/Berlin
Contact:

Post by Guido Körber »

Die Seriennummer 0 wurde nie in einem Chip verwendet, also auch kein Problem.
Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Post by Robert Marquardt »

0 als Parameter dafuer geht nicht. Man moechte ja unterscheiden koennen zwischen "alle IO-Warrior ob Seriennummer oder nicht" = $FFFFFFFF und "nur IO-Warrior ohne Seriennummer" = 0.

Da kommt die Idee auf die Seriennummer in der Info-Struktur nicht nur als Zahl sondern auch als ASCII- und Unicode-String anzubieten. Dem Programmierer die Infos vorzukauen kann nicht schaden. Ich habe da ja auch schon Fehler gemacht (%d statt %x im sscanf).
Robert Marquardt
Posts: 543
Joined: Mon Dec 01, 2003 6:09 pm

Post by Robert Marquardt »

Die Funktion zum Belegen eines IO-Warrior habe ich nun IowKit2UseDevice() genannt.

Code: Select all

BOOL IowKit2UseDevice(IOWKIT2_HANDLE devHandle, BOOL Use, PIOWKIT2_REPORT Mask);
Der Mask-Report dient als Maske zum Belegen der Pins.
Bei den Teilgeraeten kommt natuerlich das IO-Pins-Teilgeraet im DeviceChange zuerst, da die Belegung von Pins Auswirkung auf die anderen Teilgeraete hat. Wenn IO-Pins von Special Modes belegt werden, so wird natuerlich das zugehoerige Teilgeraet garnicht mehr angeboten.

Eine Unterscheidung von "Teilgeraet belegt" und "Teilgeraet aktiv" ist wohl nicht noetig. Das wuerde ja eine Schaltung implizieren, die es erlaubt einander auschliessende Special Modes doch abwechselnd nutzen zu koennen. Da ist es wahrscheinlich billiger zwei IO-Warrior zu benutzen.
Post Reply