Device driver per OS/2

Transcript

Device driver per OS/2
Device driver per OS/2
Consigli e considerazioni
Tratti dalla Developer Connection News
Autore: Steve Mastrianni (IBM)
Traduzione e adattamento per il PIDO/2: Mentore Siesto
Marzo 2007
Note del traduttore. Questi articoli risalgono ai primissimi anni ’90, quando la versione ufficiale di OS/2 era la
1.3 e il divorzio tra IBM e Microsoft non era ancora concluso. Alcune di queste considerazioni, pertanto,
riguardano la vecchia versione a 16 bit di OS/2 e non sono applicabili direttamente alle recenti revisioni del
sistema operativo. Inoltre, questa non è una guida completa allo sviluppo di device driver per OS/2, quanto
una serie di considerazioni sullo sviluppo; solo nell’ultima parte si hanno a disposizione effettive utilità per la
programmazione. Per informazioni maggiormente complete sullo sviluppo di device driver per OS/2, si
rimanda al libro di Mastrianni “Writing OS/2 2.1 device drivers in C”, che eventualmente verrà tradotto per il
PIDO/2 (si tratta di un tomo particolarmente ponderoso, di oltre 700 pagine) e alla documentazione già
tradotta per il PIDO/2 in merito.
Indice Generale
Pag.
Racconti dalla trincea
3
Assemblaggio richiesto: i device driver per OS/2
6
Finalmente, arrivano gli strumenti di programmazione per OS/2 2.0
14
Confessioni di uno sviluppatore DDK
18
OS/2 ha supporto ai device
21
Scrivere device driver – come ottenere il meglio da OS/2
22
Scrivere device driver: da dove partire?
23
Scrivere device driver – una breve occhiata a OS/2 SMP
25
Scrivere device driver – Interrupt
27
Scrivere device driver – basi del Plug & Play
28
Scrivere device driver – segmenti multipli
29
2
Steve Mastrianni è un Industry Consultant, specialista in device driver e applicazioni real – time per
OS/2. Autore del libro “Writing OS/2 2.1 device drivers in C”, Steve è considerato uno dei
maggiori esperti nel campo di OS/2 e dei device driver per OS/2. Steve è raggiungibile su
CompuServe@733534,746 oppure in Internet come [email protected].
I consigli dell’esperto
OLTRE IL DOS: WINDOWS e OS/2
Racconti dalla trincea
Discorsi di un programmatore specializzato nella scrittura di device driver per OS/2
Autore: Steve Mastrianni
Nel gennaio del 1989, stavo effettuando una presentazione su OS/2 ai rappresentanti di un possibile
cliente. Costoro avevano un sistema basato su DOS per l’acquisizione dati, che mancava della
capacità di acquisire ed elaborare i dati in contemporanea. Fatte le loro considerazioni, avevano
concluso che OS/2 avrebbe potuto fare al caso loro, ma ancora non erano convinti che ciò non
sarebbe stato possibile con Unix.
L’applicazione era perfetta per OS/2. Il sistema doveva controllare le transazioni sul bus seriale e i
livelli di tensione in tempo reale, e doveva agire immediatamente al verificarsi di certe condizioni.
Questo portava subito Unix fuori dal discorso, dato che esso manca di prelazione. Tutto andava
bene, fino a che uno degli ingegneri senior pose la domanda ovvia: “Ovviamente possiamo avere
device driver per il nostro hardware specializzato, no?”. Feci un po’ di scena e passai ad altri
argomenti, promettendo di tornare sul discorso dei driver in seguito.
Chiamando i rivenditori hardware, ho avuto da tutti la stessa risposta: “Ci dispiace, abbiamo solo i
driver per DOS. Vorremmo supportare OS/2, ma non abbiamo chi sappia come scriverli. Sappiamo
che sono molto difficili da scrivere, e solo pochi clienti, comunque, ce ne hanno richiesti.”. Decisi
di capire il perché: perché dovrebbe essere così difficile scrivere un driver per OS/2?
E dunque, feci le valigie e andai alla Microsoft University per il corso di scrittura di device driver
per OS/2. Il corso durò una settimana, e fu uno dei più intensivi che io abbia mai seguito.
Basi dei device driver.
Quando un’applicazione OS/2 deve effettuare un’operazione di I/O, essa formula una richiesta di
I/O al kernel. Il kernel verifica la richiesta, la traduce in un pacchetto di richieste al driver, e chiama
il device driver per il servizio. Il driver gestisce tutti i dettagli dell’hardware: indirizzamento I/O,
temporizzazione, impostazione dei registri, gestione degli interrupt e controllo degli errori. Quando
il dispositivo risponde, il driver organizza e formatta i dati in un formato che l’applicazione possa
riconoscere, reinvia i dati o un messaggio di stato e notifica al kernel che la richiesta di servizio è
stata completata.
Se il driver non riesce a gestire direttamente la richiesta, può bloccare il thread che l’ha formulata
oppure restituire un “request not done” al kernel. In qualsiasi caso, il driver lascia libera la CPU
e permette l’esecuzione di altri thread. In caso d’errore, il driver lo invia al kernel assieme a un
messaggio di stato “request complete”.
3
Quel che rende unici i driver per OS/2 è la necessità di operare in modo reale e protetto. Gli
indirizzi calcolati nel modo reale non valgono se il sistema passa al modo protetto, e viceversa. Il
driver deve gestire le commutazioni di modo al volo. Comprendere questa operazione bimodale è la
chiave della scrittura dei driver per OS/2 1.x. Molte routine Device Helper (DevHlp) supportano le
operazioni bimodali, ma imparare come organizzarle può aiutare molto.
Un salto in profondità
Rientrato dalla Microsoft University, ero ansioso di provare il mio primo driver. Ordinai il Device
driver development kit (DDK) da Microsoft, che contiene l’importante kernel debugger. Il KDB è
un kernel sostitutivo che, tra le altre cose, conosce le strutture dei driver. Per esempio, per mostrare
un pacchetto di richieste, basta usare il comando .d req es:bx. KDB formatta i dati e li mostra
nella forma del pacchetto di richieste. Non potete nemmeno pensare di scrivere un driver per OS/2,
senza questo strumento!
Iniziai con un semplice driver nullafacente, basandomi sugli esempi dati nel corso: esso funzionò
perfettamente. A quel punto iniziai con il vero problema: i miei clienti necessitavano di un driver
per una scheda A/D a otto canali. La scheda usava un controller intelligente pilotato a interrupt e
permetteva i trasferimenti in DMA. Scartabellai furiosamente tra la mia documentazione da
studente per cercare esempi di come implementare un simile driver, inutilmente. Non vi erano
esempi di gestori di interruzione, niente sul DMA, niente sulle funzioni di controllo dell’I/O definite
dall’utente.
Microsoft, chiamata da me, mi rinviò a Compaq (che forniva la versione di OS/2 in mio possesso),
la quale mi rimandò a Microsoft. Cercai nelle librerie di informatica senza successo: alla fine, mi
rimboccai le maniche e cominciai a sperimentare.
Il driver fa un lavoro semplice – in linea di principio. Esso deve gestire le richieste del kernel e
restituire i risultati all’applicazione. Un driver per OS/2 riceve due tipi di richieste: alcune possono
essere completate immediatamente, altre no. Le richieste vengono realizzate tramite una struttura
dati standard chiamata pacchetto richieste (request packet). Il kernel manda al driver un puntatore
bimodale al request packet. Dato che il driver deve poter operare in modalità reale o protetta, il
puntatore bimodale assicura che il request packet sia accessibile in entrambi i modi.
Quando una richiesta non può essere gestita istantaneamente (es., nel caso di una ricerca su disco).
Il driver (tramite alcune routine DevHlp) la pone in una coda. I driver per dischi possono scegliere
se ordinare le richieste pendenti per ricerche su disco in ordine di settore, per minimizzare il tempo
di ricerca.
L’architettura a thread di OS/2 dà una responsabilità extra al programmatore. Quando un driver non
riesce a gestire una richiesta all’istante, esso blocca il thread richiedente; al completamento della
richiesta, il driver sblocca il thread.
Strumenti di sviluppo
Il DDK arriva con un raccoglitore ad anelli contenente le strutture dei driver, le descrizioni delle
routine DevHlp, e le istruzioni per l’uso del KDB. Solo le prime 40 pagine circa sono state utili. Il
libro descrive le DevHlp in dettaglio, ma non contiene esempi di driver funzionanti.
Tutti i driver che scrivo, inclusi i gestori di interruzione, sono realizzati in C Microsoft 6.0 alla
massima ottimizzazione. Non sprecate tempo a scrivere driver in assembler. Scrivere un driver in C
richiede circa la metà del tempo che serve per lo stesso driver in assembler, ottenendo un modulo
che funziona nello stesso modo.
4
Un altro strumento utile è DDC.LIB, una libreria per device driver chiamabile dal C e distribuita da
Pentasoft (17541 Stone Ave. N, Seattle, WA 98133, (206) 546-0470). Forse la funzione più
importante dalla DDC.LIB è Transfer, che trasporta i dati tra i driver e le applicazioni e tiene
conto del cambio di modalità durante il trasferimento. DDC.LIB gestisce il trasferimento di dati
dalla memoria virtuale a quella fisica e viceversa, da virtuale a virtuale e da fisica a fisica. Se
intendete lavorare seriamente sullo sviluppo di driver per OS/2, questa libreria è obbligatoria.
La luce alla fine del tunnel?
Chiunque abbia scritto driver per altri sistemi operativi multitasking (come Unix o VMS) avrà
buone basi per la scrittura di driver per OS/2. Microsoft stima che un programmatore C esperto che
abbia seguito il corso alla Microsoft University impiega da quattro a sei mesi per scrivere il suo
primo driver per OS/2. Driver successivi impiegano da due a quattro mesi: i disk driver sono più
impegnativi e possono richiedere più tempo.
Per il mio primo driver ho impiegato circa tre mesi. Il successivo ne richiese due, e in seguito riuscii
a scrivere alcuni semplici driver in una settimana circa, quindi la cosa si semplifica con la pratica.
Anche se i driver per OS/2 divengono più comuni al tempo attuale, la situazione è comunque
grama. Per la maggior parte sono driver specializzati e non immediatamente disponibili. Quel che
serve sono driver standard di uso generico, adattabili a hardware diverso. Per esempio, mi
piacerebbe vedere un driver per CD-ROM per OS/2, o per schede fax o nastri magnetici, ma ancora
non ne vedo. Perché? Ci sono certamente più clienti, adesso, che richiedono driver per OS/2. Senza
di essi, il sistema operativo prescelto potrebbe non essere OS/2.
OS/2 2.0 non semplificherà la scrittura dei device driver. Certamente, sparirà il modo reale, per cui
il driver non dovrà occuparsi delle operazioni bimodali. L’architettura dei driver DOS però
cambierà radicalmente. I programmi DOS chiameranno un Virtual Device Driver, invece che
accedere direttamente all’hardware. Il VDD gestirà la richiesta e la manderà al device driver fisico
(PDD). Il PDD effettuerà le comunicazioni a basso livello con il device e rinvierà i dati al VDD.
L’interfaccia VDD è nuova, mentre il PDD altro non è che il meccanismo standard bimodale di
OS/2 1.x, privo della parte in modalità reale. Il VDD esamina il BIOS e le altre funzioni di
interrupt, permettendo così a un’applicazione DOS di pensare di stare parlando con il dispositivo,
mentre essa sta in realtà comunicando con il VDD. Le applicazioni che funzionano in modo
protetto continueranno a chiamare i driver per OS/2, come nella versione 1.x, ma potranno usare
l’indirizzamento lineare 0:32.
A giugno, Microsoft ha annunciato una nuova architettura per device driver per memorie di massa,
chiamata LAyered Device DRiver architecture (LADDR). Microsoft asserisce che la LADDR possa
ridurre del 90 % il tempo necessario a realizzare un sistema di memorie di massa. Spero che ciò sia
vero, ma in base a quanto ho visto finora non ci scommetterei la casa.
Un nuovo DDK porterà con sé codice standard per i driver, per cui lo sviluppatore dovrà solo
aggiungere il codice specifico del device per implementare un driver completo. Non ho ancora visto
il nuovo DDK, quindi non posso verificare le affermazioni di Microsoft. Alla data della scrittura,
Microsoft non ha ancora una data di rilascio per il kit LADDR. I driver non destinati alle memorie
di massa continueranno a essere scritti con i metodi convenzionali.
IBM e Microsoft non hanno ancora fatto abbastanza per aiutare coloro che cercano di realizzare i
driver di cui OS/2 ha tanto bisogno. L’aggiornamento del DDK dalla versione 1.1 alla 1.2 è molto
indietro con i tempi, e anche l’NDDK, usato per sviluppare i driver per schede di rete per la
Extended Edition, è in ritardo. Il DDK versione 1.1 non funziona sulle macchine PS/2, per cui i
driver vanno sviluppati per l’architettura ISA.
5
Le informazioni sono ancora grezze e incomplete. Sono apparsi molti libri, ma nessuno di essi
fornisce esempi di device driver in C. La maggior parte della documentazione disponibile descrive
le funzioni DevHlp e le loro sequenze di chiamata, ma non come organizzarle in un driver.
Quel che serve è una guida alla scrittura di driver che demistifichi la scrittura dei device driver per
OS/2. La guida dovrebbe contenere esempi di driver scritti in C, non frammenti sparsi di codice
Assembly. Dovrebbe anche contenere una lista di utili funzioni per aiutare lo sviluppo e la
correzione di device driver. Fino a che queste informazioni resteranno indisponibili, lo sviluppo dei
device driver resterà il tallone d’Achille di OS/2.
Assemblaggio richiesto: i device driver per OS/2.
Guida allo sviluppo di un driver asincrono per terminale RS-232 per OS/2 in C.
Autore: Steve J. Mastrianni
I device driver per OS/2 continuano a essere un fattore limitante nell’accettazione e nell’uso di
OS/2. I driver per DOS abbondano, ma quelli per OS/2 sono rari come i denti in un neonato, per
vari motivi. I driver per OS/2 sono più complicati di quelli per DOS. Essi devono gestire la
commutazione di contesto e le priorità, e permettere le operazioni in due modi, reale e protetto, cose
sconosciute a molti programmatori DOS. Nell’articolo descriverò come scrivere un driver per
terminale RS-232 asincrono in C per OS/2, completo di gestione degli interrupt e supporto del timer
(il codice necessario per il driver è disponibile su BIX). Una volta vista la strada, avrete la
comprensione base per scrivere driver per OS/2 per altri dispositivi.
La natura della Bestia
I device driver per OS/2, come altri driver multitask, nascondono alle applicazioni le caratteristiche
fisiche dei dispositivi di I/O (come la temporizzazione o l’indirizzamento delle porte di I/O).
Un’applicazione che richiede un servizio di I/O manda una richiesta al kernel, che a sua volta
chiama un driver. Il driver gestisce tutti i dettagli hardware, come l’impostazione dei registri, la
gestione degli interrupt e il controllo degli errori. Quando la richiesta è completa, il driver prepara i
dati in un formato riconoscibile dall’applicazione. Esso poi manda i dati o un messaggio di stato
all’applicazione, e notifica al kernel il completamento dell’operazione. Se la richiesta non può
essere gestita al momento, il driver può bloccare il thread oppure restituire un messaggio di Request
Not Done al kernel: in ogni caso, il driver libera la CPU e permette l’esecuzione di altri thread.
I device driver per Dos non hanno una controparte OS/2 diretta. Essi sono semplici driver monotask
in polling. Anche i driver a interrupt sotto DOS eseguono il polling fino a che l’elaborazione
dell’interrupt non viene completata. I device driver per DOS supportano una richiesta per volta, e
altre richieste successive dal kernel DOS portano al crash del sistema.
Al contrario, un driver per OS/2 deve gestire il sovrapporsi di richieste provenienti da diversi
processi e thread, per cui dev’essere rientrante. Deve anche gestire gli interrupt dal dispositivo e
quelli provenienti dal gestore del timer. Inoltre, il driver per OS/2 deve sovrintendere alle
commutazioni tra modo reale e protetto. Queste operazioni devono essere portate a termine con
efficienza, permettendo ad altri thread di avere accesso alla CPU, e soprattutto, deve lavorare in
maniera affidabile. Dato che opera a livello 0, il driver per OS/2 è il solo programma che ha accesso
alle funzioni critiche del sistema (es., il sistema di interrupt e il timer). Il driver, pertanto, dev’essere
affidabile, dato che qualsiasi errore in esso può causare un crash dell’intero sistema.
6
I device driver per OS/2 devono essere anche bimodali, ossia in grado di operare in modalità reale e
protetta (per OS/2 1.3). È necessario continuare a elaborare gli interrupt, e le richieste devono essere
completate anche se l’utente rientra nel prompt di OS/2 dal DOS compatibilità box e viceversa. I
driver devono poter essere disinstallati a richiesta, liberando la memoria utilizzata. Inoltre, i driver
possono supportare monitor per il dispositivo, ossia programmi che controllano i dati che passano
dal driver al kernel e viceversa. Fortunatamente OS/2 fornisce una vasta gamma di servizi di
sistema chiamati routine Device Helper, o DevHlp, che forniscono queste funzionalità.
Gli strumenti dell’officina
Sviluppare un driver per OS/2 richiede una comprensione completa del ruolo di un device driver,
oltre a una solida conoscenza del sistema operativo e della filosofia di OS/2. Il debug di driver OS/2
può essere difficile, anche con gli strumenti adatti. Il driver opera a livello 0, con accesso totale
all’hardware di sistema. Però, esso quasi non ha accesso ai servizi di supporto di OS/2, eccezion
fatta per una manciata di routine DevHlp. Molte volte si hanno fallimenti nei driver in un contesto
di tempo reale, come nel mezzo della gestione di un interrupt. Può essere difficile, se non
impossibile, trovare un problema in un driver con le normali tecniche di debug. In tali casi è
necessario visualizzare il lavoro del driver e di OS/2 al momento dell’errore, per poter rilevare il
problema e correggerlo.
Lo strumento più importante in assoluto è il debugger per il driver. Generalmente io uso il kernel
debugger di Microsoft, presente nel DDK. Molte altre compagnie offrono buoni strumenti per lo
sviluppo di driver. Una versione più completa di questo articolo in forma di libro e una libreria
DevHlp completa e utilizzabile in C sono disponibili presso PSS. PentaSoft dispone di
un’interfaccia per il linguaggio C alle routine DevHlp. OS Technologies offre un debugger per
driver indipendente dalla versione di OS/2. FutureWare, infine, vende un debugger per driver e
un’interfaccia in C per le routine DevHlp.
Io scrivo tutti i miei driver, inclusa la gestione di interrupt e timer, con Microsoft C 6.0. Un driver
in C viene scritto in circa metà del tempo che impiega la sua scrittura in assembly con il Microsoft
Macro Assembler. In casi speciali, soprattutto scrivendo driver per dispositivi molto veloci o dove
le prestazioni sono assolutamente critiche, ha senso scrivere alcune routine in assembly: la massima
parte dei driver, comunque, funziona correttamente anche scritta in C.
Anatomia di un device driver per OS/2
I driver ricevono le richieste dal kernel di OS/2. Quando il driver viene inizialmente aperto tramite
una chiamata a DosOpen, il kernel restituisce uno handle al programma che ha richiesto l’accesso al
driver. Questo handle viene usato per gli accessi successivi al driver, il cui nome non è più
necessario né usato.
Quando un’applicazione chiama un driver, il kernel intercetta la chiamata e formatta la richiesta per
il driver in una struttura dati standard chiamata request packet. Il request packet contiene i dati e i
puntatori usati dal driver per far fronte alla richiesta. Nel caso di DosRead o DosWrite, per esempio,
il request packet contiene l’indirizzo fisico del buffer del chiamante. Nel caso di un’operazione di
controllo di I/O (IOCtl), il request packet contiene l’indirizzo virtuale di un buffer di dati e
parametri. In dipendenza dalla richiesta, i dati nel request packet cambieranno, ma la lunghezza e il
formato dell’intestazione del request packet rimangono costanti. Il kernel passa al driver un
puntatore bimodale al request packet. Questo indirizzo bimodale, o impilato, è un puntatore valido
in modo protetto e reale, dato che il processore potrebbe essere in uno qualsiasi di questi due modi
alla chiamata al driver.
7
Come fa il kernel a sapere quale driver chiamare? I driver vengono caricati da OS/2 a tempo di
avviamento, e il kernel mantiene un elenco dei driver installati per nome. Prima di usare un driver,
questo va aperto da parte dell’applicazione con DosOpen. Questa API specifica una stringa ASCII-Z
con il nome del device come parametro. Il kernel confronta tale nome con l’elenco dei driver
installati e, se trova il nome corrispondente, chiama la parte Open della sezione Strategy del driver
per aprire il dispositivo. Se l’operazione ha successo, il kernel restituisce uno handle
all’applicazione per l’uso futuro del driver. Il nome ASCII-Z non viene più usato, per tutto il
periodo in cui il driver resta aperto.
Gli handle al dispositivo sono di norma assegnati in sequenza, a partire da 3 (0, 1 e 2 sono riservati
a OS/2). Non è comunque buona idea dare per scontato il valore dello handle. Il nome ASCII-Z del
dispositivo invece è localizzato nell’intestazione del driver.
Il request packet di OS/2
Un device driver per OS/2 consiste di una sezione Strategy e di sezioni opzionali Interrupt e Timer.
La sezione Strategy riceve le richieste dal kernel in forma di request packet. La sezione verifica la
richiesta e, se possibile, la completa e manda il risultato al kernel. Se la richiesta non può essere
completata immediatamente, il driver può opzionalmente accodare la richiesta da completare a
tempo successivo e lancia l’operazione di I/O se necessario. Il kernel chiama la sezione Strategy
direttamente, trovando il suo indirizzo offset nell’intestazione del driver.
La prima voce nel request packet è la lunghezza dello stesso, data dal kernel. Il secondo parametro è
il codice di unità. Quando un driver supporta più unità logiche, il valore qui presente seleziona
l’unità. Il terzo campo è il codice di comando, dato dal kernel, ossia il codice usato dal comando
switch della sezione Strategy per comprendere il tipo di richiesta proveniente dal kernel. Il campo
successivo è la word di stato restituita al kernel. Questo campo conterrà il risultato dell’operazione
del driver, assieme al bit Done per notificare al kernel che la richiesta è completa (non è sempre
così: il driver potrebbe ritornare senza aver settato tale bit). Per facilitare le cose, uso un tipo unione
per accedere a tipi specifici di richiesta e piazza le strutture dei request packet in un file include.
Creare il device driver per OS/2
Un semplice device driver per OS/2 consiste di un segmento codici e un segmento dati, anche se a
richiesta è possibile allocare più memoria (grazie alle routine DevHlp). I primi dati che appaiono
nel segmento dati corrispondono all’intestazione del driver.
Tale intestazione è una struttura a lista collegata di lunghezza fissata, che contiene informazioni
utilizzabili dal kernel durante la fase di INIT e le operazioni normali. La prima voce
dell’intestazione è un puntatore al prossimo dispositivo supportato dal driver. Se nessun altro
dispositivo viene supportato, il puntatore viene posto a -1L: ciò termina l’elenco dei dispositivi
supportati dal driver. Se il driver supporta più dispositivi, come capita a una scheda seriale a quattro
porte o a un controller per dischi, il collegamento è un puntatore FAR alla prossima intestazione.
La voce successiva nell’intestazione del dispositivo è la word degli attributi, seguita da un offset di
una word alla sezione Strategy del driver. Solo l’offset è necessario, perché il driver è scritto con il
modello small con un segmento codice di 64 kiB e un segmento dati di 64 kiB (non è sempre vero:
in certi casi il driver può allocare più spazio per codice e dati, se necessario).
La voce seguente è un indirizzo offset a una routine di comunicazione interdriver, se il driver
supporta l’IDC (il bit DAW_IDC nella word di attributi del driver dev’essere settato; in caso contrario,
la chiamata ad AttachDD dall’altro driver fallirà).
8
L’ultimo campo è il nome del dispositivo, che dev’essere lungo otto caratteri. I nomi con meno di
otto caratteri vanno riempiti con spazi vuoti. Ricordate che un errore nel codificare l’intestazione
del device driver causerà il crash immediato all’avvio del sistema.
Dare al driver C un’interfaccia ai registri
I driver per OS/2 sono normalmente scritti in C con il modello di memoria small, quindi in genere
accedono a 64 kiB di codice e 64 kiB di dati. Il file .SYS del driver deve caricare il segmento dati
prima del segmento codice. Quando scrivete un driver per OS/2 in C, dovete fornire un meccanismo
per porre i segmenti codice e dati nell’ordine opportuno, e dovete dare anche un’interfaccia a basso
livello per gestire gli interrupt del dispositivo e del timer. Dato che l’intestazione del device
dev’essere il primo elemento del segmento dati, è necessario che il compilatore C non inserisca il
codice C di avvio prima dell’intestazione. Potreste dover inoltre fornire un metodo per rilevare
quale dispositivo viene richiesto per driver che supportano più unità. Il breve programma assembly
nel listato 4 si occupa di queste richieste: l’entry point _actrused impedisce l’inserimento del
codice di avvio C prima del segmento dati del driver. Le direttive di ordinamento dei segmenti
assicurano che il segmento dati preceda quello codice.
Notate l’entry point _STRAT. Come viene chiamato? Ricordate che questo è l’indirizzo posto
nell’intestazione del device, nel segmento dati del driver. Quando il kernel fa una richiesta al driver,
esso cerca questo indirizzo nell’intestazione del device e realizza una chiamata FAR a tale
indirizzo. La routine in assembly, quindi, chiama la linea C. In tal modo viene stabilito il
collegamento tra il kernel e il driver.
Perché vi è un push 0 all’inizio della routine _STRAT? Quello è il numero del dispositivo. Ogni
dispositivo supportato dal driver richiede un’intestazione separata, ognuna delle quali contiene un
suo indirizzo offset per la propria sezione Strategy. Usando l’interfaccia assembly, la routine spinge
nello stack il numero del dispositivo e lo passa alla sezione Strategy del driver.
La sezione Strategy
Questa sezione è né più né meno di un grosso switch. Le richieste comuni al driver, come DosWrite
e DosRead, hanno codici di ritorno e funzionamento standard. Il driver può ignorare una o tutte le
richieste restituendo uno stato Done al kernel: ciò dice al kernel che la richiesta è stata completata.
Lo stato restituito al kernel può includere anche informazioni sugli errori, che il kernel restituisce
quindi al chiamante.
Notate che in caso di funzionamento standard, il kernel mapperà il valore di errore restituito dal
driver in uno dei codici di ritorno standard. Pertanto, è impossibile passare un codice speciale di
ritorno all’applicazione tramite una richiesta standard al driver. Se tenterete di farlo, il kernel
intercetterà il codice di ritorno speciale e lo mapperà a un codice standard. Il solo modo di restituire
un codice speciale all’applicazione è l’uso di una richiesta IOCtl. Gli IOCtl sono usati per
operazioni speciali del driver (I/O di porta, per esempio). Gli IOCtrl vengono accessati quando
l’applicazione lancia una chiamata a DosDevIOCtl con l’handle del dispositivo. Questa flessibilità
consente al programmatore di personalizzare il driver per ogni dispositivo. Per esempio, avendo un
driver seriale che controlla il traffico e riporta l’occorrenza di caratteri speciali, si può usare una
lettura IOCtl e passare il carattere nel codice di ritorno.
Nel listato 5 è presentato lo scheletro di una sezione Strategy. Notate lo switch sul comando request
packet. Parecchie funzioni standard dei driver hanno codici di comando predefiniti in OS/2. Il
programmatore può agire sulle richieste o ignorarle a piacimento. Nonostante non abbia senso, il
driver potrebbe ignorare il comando Open lanciato dal kernel in risposta a una chiamata a DosOpen.
9
O, più logicamente, il driver può rifiutare la richiesta di de-installazione lanciata tramite una
Deinstall.
La chiamata a INIT viene fatta una sola volta, durante il caricamento del sistema operato dalla linea
DEVICE = in CONFIG.SYS. La chiamata viene fatta in modo INIT da ring 3, con privilegi di I/O. La
routine INIT è il punto in cui si inserisce il codice per inizializzare il dispositivo, come la
configurazione della UART oppure l’invio del disco alla traccia 0.
La primissima cosa da fare nell’inizializzazione è salvare l’indirizzo dell’entry point DevHlp nel
segmento dati del driver. Questo è l’unico momento in cui l’indirizzo è valido. Esso va salvato, o
verrà perso irrimediabilmente. L’indirizzo dell’entry point DevHlp viene passato nel request packet
INIT. Il codice di inizializzazione fa altre due cose. Innanzitutto, manda su schermo il messaggio di
tentato caricamento del driver. Quindi, trova l’indirizzo del segmento dell’ultimo elemento codice e
dell’ultimo elemento dati, e li rinvia a OS/2. Questi usa i valori ricevuti per dimensionare la
memoria. Se un driver non completa l’installazione, deve rimandare indietro i valori 0 per CS e DS,
in modo che OS/2 possa riutilizzare lo spazio lasciato.
Una delle tecniche più comuni nella realizzazione di driver per OS/2 è che la sezione Strategy
richieda servizi dal dispositivo e aspetti che un interrupt (device o timer) segnali il completamento
della richiesta. Il frammento del listato 6 mostra un’implementazione dello schema per la funzione
Read del mio driver di comunicazione seriale d’esempio. In tal caso, la sezione Strategy inizia l’I/O
e lancia una chiamata alla DevHlp Block, che blocca il thread chiamante. Quando il dispositivo
segnala il completamento dell’operazione tramite interrupt, la sezione lancia il thread bloccato e
completa così la richiesta. Per proteggersi dal rischio di richieste mai completate (dispositivo
spento, per es.), la chiamata a Block può contenere un parametro di timeout: se il tempo prima
dell’interrupt di completamento supera il valore limite, la sezione Strategy può mandare l’errore
corretto al kernel.
Un altro modo per dare un timeout al dispositivo è usare la routine SetTimer delle DevHlp. Si può
attaccare un gestore di timer all’orologio di sistema di OS/2 e lasciare che il gestore lanci il thread
bloccato dopo un numero di tick specificato.
I comandi consentiti dalla sezione Strategy sono lasciati al programmatore. Si possono elaborare
solo i comandi che si desidera elaborare, lasciando semplicemente passare gli altri inviando uno
stato Done al kernel. Altrimenti si possono intrappolare le chiamate illecite restituendo al kernel un
errore ERROR_BAD_COMMAND. Tenete a mente, peraltro, che il kernel lancia spesso i suoi comandi al
driver, a vostra insaputa. Per esempio, quando l’utente di un’applicazione che aveva aperto un
driver preme Ctrl-C, il kernel controlla l’elenco dei driver aperti dall’applicazione e manda una
richiesta Close a tutti. In generale, ho notato che è più semplice ignorare tutte le richieste che non si
attendono e marcarle come eseguite.
Nel driver più semplice, la sezione Strategy può contenere le sole richieste Open, Close e Read o
Write. In un driver complesso come quelli per unità a disco, la sezione Strategy può contenere oltre
due dozzine di funzioni standard e parecchie chiamate IOCtl addizionali. Queste sono
effettivamente funzioni Strategy, ma sono ulteriormente suddivise per permettere operazioni
specialistiche del dispositivo o maggiormente dettagliate. Per esempio, un driver potrebbe inviare
un elenco di parametri a una porta di I/O per inizializzarla, e restituire il valore d’ingresso di una
porta di stato con lo stato dell’inizializzazione.
10
Un campione delle funzioni standard del driver
INIT (codice 0x00). Questa funzione viene chiamata dal kernel all’inizializzazione del driver, al
momento dell’avvio del sistema. La sezione INIT deve inizializzare il dispositivo, come per
esempio impostare il baud rate, la parità e altro per quanto riguarda una porta seriale, oppure
controllare che il dispositivo sia installato, lanciando una richiesta di stato al controller. La funzione
INIT viene chiamata in una modalità speciale del livello 3, con alcune funzioni del livello 0.
Il driver potrà disattivare gli interrupt, che però vanno ripristinati prima di rientrare al kernel. Il
codice di INIT può effettuare I/O diretto sulle porte senza violazione di protezione. Solitamente, il
programmatore del driver allocherà buffer e salvataggio dei dati durante l’inizializzazione, per
accertarsi che il driver funzioni dopo l’installazione. Dato che l’inizializzazione avviene a livello 3,
il sistema può controllare per assicurarsi che il buffer e l’allocazione di memoria siano validi e che i
segmenti siano di proprietà del driver. In caso contrario il driver può rimuoversi dalla memoria,
liberando tutto lo spazio precedentemente allocato per altri componenti di sistema. Dato che
l’inizializzazione avviene una sola volta all’avvio, non è indispensabile ottimizzarla. Fate qui tutte
le inizializzazioni necessarie, dato che potrebbe essere proibitivo o addirittura impossibile farle
durante l’uso del driver.
Media Check (codice 0x01). La funzione viene chiamata dal kernel prima dell’accesso ai dischi, e
pertanto è valida solo per dispositivi a blocchi. Il kernel passa al driver il byte del media ID
corrispondente al tipo di disco che aspetta di trovare nell’unità selezionata.
BuildBPB (codice 0x02). Quando il driver di blocco riceve una chiamata Build Bios Parameter
Block, deve restituire un puntatore al BPB che descrive l’unità di memoria di massa.
Read (codice 0x04). L’applicazione chiama la sezione Read lanciando l’API DosRead con l’handle
ottenuto tramite DosOpen. La routine Read può restituire un carattere per volta, ma più spesso
restituisce un buffer di dati. Come la funzione Read lavori, sta al programmatore deciderlo. Il driver
restituisce il conteggio di caratteri letti e salva i dati ricevuti nel segmento dati dell’applicazione.
Read restituisce un codice di ritorno driver standard.
Nondestructive Read (codice 0x05). In risposta a questa richiesta, il driver deve leggere il primo
carattere del buffer del driver e restituirlo al chiamante. Se non sono presenti caratteri, il driver
ritorna immediatamente con il bit Done e gli opportuni bit d’errore impostati.
Input Status (codice 0x06). Il driver deve azzerare il bit Busy nel request packet se uno o più
caratteri sono nel buffer del driver, o impostato se nessun carattere è presente. Si tratta di una
funzione Peek (osserva) per determinare la presenza di dati.
Flush Input Buffer(s). Dovrebbe svuotare le code dei ricevitori o i buffer, e restituire uno stato
Done al kernel.
Write (codice 0x08). Richiesta standard effettuata dall’applicazione a seguito di una chiamata
all’API DosWrite. L’applicazione passa al driver l’indirizzo dei dati da scrivere (solitamente nel
segmento dati dell’applicazione) e il numero di caratteri da scrivere. Il driver scrive i dati e
restituisce all’applicazione lo stato, assieme al numero di caratteri scritti. Write restituisce un codice
di ritorno standard da driver.
Write with Verify (codice 0x09). Il driver scrive dati come in Write, ma verifica la scrittura.
11
Output Status (codice 0x04a). Il driver imposta il bit Busy nel request packet se un’operazione è
attiva, e lo azzera se l’operazione è completata e il trasmettitore è libero.
Output Flush (codice 0x0b). Il driver svuota le code di uscita e i buffer, e restituisce lo stato Done
al kernel.
Device Open (codice 0x0d). Chiamata come risultato della chiamata a DosOpen da parte
dell’applicazione. Il kernel prende nota della richiesta a DosOpen, e se non vi sono errori manda
all’applicazione uno handle da usare per i servizi successivi. Il programmatore può usare questa
sezione per inizializzare un dispositivo, svuotare i buffer, resettare il puntatore ai buffer,
inizializzare le code o qualsiasi cosa sia necessaria per la corretta operatività.
Device Close (codice 0x0e). Chiamata come risultato dell’API DosClose usata dall’applicazione
con il corretto handle di dispositivo. È bene assicurarsi che l’applicazione che chiude il driver sia la
stessa che l’ha aperto, quindi salvate il PID dell’applicazione che ha aperto il driver e fate in modo
che il PID usa in chiusura siano identici. In caso contrario rigettate la richiesta. Dovreste avere tutti
i dispositivi in quiescenza, a questo punto.
Removable Media (codice 0x0f). Il driver riceve questa richiesta quando un’applicazione genera
una chiamata a IOCtl categoria 8, funzione 0x20. Invece di chiamare l’IOCtl, il kernel lancia la
richiesta citata. Il driver deve impostare il bit Busy dello stato del request packet se l’unità non è
rimovibile, o azzerarlo in caso contrario.
Generic IOCtl (codice 0x10). Questa è una chiamata speciale. È molto flessibile, poiché i dati
passati al driver vengono salvati in due buffer di proprietà del chiamante. Tali buffer possono
contenere qualsiasi tipo di dati; il formato è lasciato al programmatore.
Il primo e il secondo parametro di un IOCtl sono l’indirizzo del buffer dei dati e dei parametri del
programma applicativo, rispettivamente. Il buffer dei parametri potrebbe contenere un elenco di
USHORT, UCHAR o puntatori. Il parametro buffer dati potrebbe essere l’indirizzo di un buffer per dati
nel programma applicativo, dove il driver salverebbe dati provenienti dal dispositivo.
Gli IOCtl possono estendere la gamma di informazioni di stato che i driver possono mandare alle
applicazioni. Supponete, per esempio, che un driver necessiti di riportare a un’applicazione il
formato (ASCII o binario) dei dati, o di dover segnalare un errore di parità in ricezione. Un IOCtl
potrebbe essere la risposta. Questo perché il kernel modifica i codici di ritorno dalle chiamate a
funzioni standard per rientrare nelle definizioni di errore standard. L’IOCtl, comunque, passerà i
codici direttamente all’applicazione, esattamente come sono stati impostati nel driver. In molti
driver scritti da me, le sezioni DosRead e DosWrite della sezione Strategy sono commentate e mai
utilizzate. Uso gli IOCtl per letture e scritture, per consentire al driver di comunicare con
l’applicazione senza che il kernel interferisca.
PrepareForSysShutdown. La funzione dice al driver di postare tutti i buffer aperti verso i
dispositivi comandati, prima della chiusura del sistema. Questo si ha quando si seleziona Chiusura
dalla finestra della Scrivania.
La sezione Interrupt
Quando OS/2 chiama il gestore di interruzioni, lo fa con gli interrupt disabilitati, per cui il tempo
passato nel gestore di interruzioni potrebbe causare problemi alle prestazioni. Se viene attivato in
risposta alla ricezione di dati, il gestore deve salvare i dati e uscire rapidamente. Per dispositivi a
carattere, la libreria DevHlp supporta letture e scritture rapide a code circolari. Per dispositivi a
12
blocchi, la gestione degli interrupt è veloce perché l’interrupt viene di norma causato dal
completamento di un’operazione di DMA o di ricerca su disco. I dati quindi vengono, di norma,
trasferiti al buffer utente usando il DMA, eliminando il bisogno di trasferire dati durante
l’elaborazione degli interrupt. Su un trasferimento DMA, il driver può uscire quando il controller
DMA inizia, in modo da lanciare altri thread. Al completamento dell’operazione, si ha una DMA
completion, che attiva il gestore di interrupt del driver.
La routine di gestione degli interrupt non è difficile da capire o scrivere, ma può divenire molto
difficile correggerla. Un errore in tale sezione spesso compare solo in un contesto in tempo reale,
quando il gestore è attivo in risposta a un interrupt hardware. Non si può lanciare printf() dalla
routine di interrupt, o ispezionare le variabili con un debugger per applicazioni. Si deve invece
usare il Kernel debugger di OS/2 dal DDK, o un debugger simile. Anche usando il KDB, un
breakpoint ferma il programma, e ulteriori interrupt potranno passare non rilevati mentre decidete
cosa scrivere. A causa di questa pausa nell’esecuzione, si perde il contesto di tempo reale del
programma, che potrebbe essere la radice del problema. In definitiva, non vi è sostituto dell’abilità
di visualizzare l’operazione corretta del gestore di interruzioni.
Il gestore del timer
In un driver per OS/2, l’interrupt del timer di sistema può essere agganciato tramite una chiamata
alla funzione DevHlp SetTimer. Si passa a OS/2 un puntatore NEAR al gestore del timer, e per
ogni tick del timer, OS/2 chiama la routine e altri gestori di timer registrati in precedenza.
Se non appaiono dati entro uno o due tick da 32 ms, il driver suppone che l’ingresso dati si sia
fermato o sia in pausa. Se è in attesa una richiesta di lettura, esso invia i dati alla sezione Strategy
bloccata lanciando una richiesta Run con lo stesso ID usato per bloccare il thread richiedente. La
sezione Strategy viene sbloccata, riceve i dati dalla coda del ricevente, e li invia al buffer dati
dell’applicazione.
Serve davvero un device driver?
Forse no. OS/2 1.x consente ai programmi con privilegi di I/O (IOPL) di fare I/O diretto verso un
dispositivo. Per una porta parallela, o un interruttore digitale, un driver potrebbe non essere
necessario. Si possono impostare o azzerare bit tramite istruzioni IN e OUT, e fintanto che il
dispositivo non è critico alle temporizzazioni, il metodo è sufficiente.
Peraltro, dispositivi che generano interrupt, richiedono servizio asincrono, oppure operano in
ambienti time critical, devono usare i device driver. Considerate come esempio una porta seriale.
Sarebbe difficile, se non impossibile, leggere dati dal dispositivo tramite IOPL. Per definizione
infatti i dati asincroni possono arrivare in qualsiasi momento. Dato che OS/2 potrebbe star facendo
girare un altro thread al momento, le probabilità di perdere dati sono molto elevate. Un driver a
interrupt potrebbe invece leggere e bufferizzare i dati in arrivo fino a che lo scheduler di OS/2 non
lanciasse il thread che legge i dati.
Opzionalmente, si può fare in modo che gli interrupt fermino il thread in esecuzione e lancino il
vostro immediatamente. Non è necessario attendere lo scheduler. Questo tipo di multitasking
prelazionale è unico in OS/2 rispetto a sistemi come Unix. In Unix il programma in esecuzione
attualmente mantiene il controllo della CPU fino a esaurire il suo time slice. Ecco perché OS/2 è la
scelta obbligata per le applicazioni time critical.
13
Compagnie citate:
FutureWare, Inc.
78 Temple Ave., Suite 15
Hackensack, NJ 07601
(201) 343-3921
Microsoft Corp.
One Microsoft Way
Redmond, WA
(800) 227-6444
OS Technologies
532 Longley Rd.
Groton, MA 01450
(508) 448-9653
PentaSoft
17541 Stone Ave. N
Seattle, WA 98133
(206) 546-0470
PSS Corp.
290 Brookfield St.
South Windsor, CT 06074
(203) 644-4764
Finalmente, arrivano gli strumenti di programmazione per OS/2 2.0
Numerosi strumenti di sviluppo per OS/2 2.0 cominciano finalmente ad apparire. Ora che IBM ha
rilasciato OS/2 2.0, iniziano a comparire anche gli strumenti di sviluppo. La versione a 32 bit di
OS/2 riesce a far girare pressoché qualsiasi programma disponibile per DOS, Windows e OS/2. A
parte alcuni bug nell’installazione, OS/2 2.0 funziona bene. È facile e veloce da usare, e offre
un’ampia gamma di opzioni di configurazione prima d’ora mai supportate. Meglio che mai, gli
sviluppatori hanno a disposizione gli strumenti che servono.
Versioni beta
L’IBM ha capito presto che doveva fornire strumenti di sviluppo adatti a OS/2 2.0. Prima erano
disponibili solo strumenti a 16 bit, e il solo compilatore di IBM, il C/2, si basava su una versione
obsoleta del compilatore Microsoft C. Durante lo sviluppo del compilatore IBM C, sono state
rilasciate alcune versioni beta, insieme a un ambiente di sviluppo e debug chiamato WorkFrame/2.
Per gli sviluppatori professionisti, il programma beta di OS/2 2.0 fu un incubo. I rilasci del toolkit
erano in ritardo rispetto al sistema, per cui sviluppare software con l’ultima beta degli strumenti
significava usare una versione vecchia del sistema. La documentazione dei toolkit beta era piena di
errori e omissioni, e alcuni manuali avevano riferimenti a funzioni obsolete di OS/2 1.x. L’IBM
batteva il tamburo dei 32 bit, ma non era in grado di dare agli sviluppatori versioni tempestive degli
strumenti necessari a realizzare le nuove applicazioni a 32 bit.
14
IBM informò tutti tramite mailing list e le conferenze di Compuserve OS2DEV e IBMOS2. La
compagnia non tentò di mascherare o nascondere problemi delle versioni beta e non richiese accordi
di riservatezza per ottenere il codice. Fu un cambio rivitalizzante, molto utile per ristabilire la
credibilità di IBM presso gli sviluppatori.
I primi passi
Pochi mesi dopo la versione GA di OS/2 2.0, IBM iniziò a distribuire la versione 1.0 del
compilatore C a 32 bit IBM CSet/2, come parte dello IBM C Developer’s WorkSet/2. In tale
ambiente erano inclusi il Presentation Manager Debugger IPMD, WorkFrame/2, il kernel debugger,
DLL di esempio, il programma PMSpy, il compilatore delle risorse, i file include, strumenti per la
migrazione e molti esempi di codice ben scritti.
WorkFrame/2 è un ambiente plug-in che permette di specificare il compilatore, l’assembler, il
debugger e il linker da usare. Permette di impostare le opzioni di compilazione, link e debug a
qualsiasi valore si voglia. Può generare makefile e librerie, ed è configurabile tramite una semplice
finestra. WorkFrame/2 funziona gestendo progetti, piuttosto che file e directory.
Un progetto viene creato inserendone i parametri nella finestra Project Control. Qui vanno
informazioni come nome e descrizione del progetto, nome dei makefile, e opzioni di
compilazione/link/lancio. Inserite le informazioni sul progetto, si può realizzare una nuova versione
del codice cliccando sul bottone “new” nella finestra Project Control.
CSet/2 è un compilatore ottimizzante completamente conforme alle specifiche ANSI 1989, che
genera codice a 32 bit per OS/2 2.0. Il compilatore supporta la coesistenza 32 bit – 16 bit a tempo di
esecuzione, librerie statiche e dinamiche, HPFS e I/O mappato in memoria. Fornisce supporto alla
migrazione per convertire programmi da OS/2 1.x a OS/2 2.0, e produce codice veloce, leggero e
consistente. Non è possibile usarlo per realizzare device driver fisici o virtuali, ma è perfetto per le
applicazioni a 32 bit. IPMD rende elementare il debug di applicazioni a 32 bit.
Peraltro, il prezzo iniziale di 895 dollari del WorkSet/2 irritò molto gli sviluppatori, che costrinsero
IBM a ridurre il prezzo a 295 dollari fino al primo settembre. Al momento del comunicato stampa
non è chiaro se IBM intenda estendere tale politica.
Supporto del ISV locale
IBM ha anche cominciato a supportare meglio i venditori di software indipendenti (ISV), critici per
il successo di OS/2 2.0. Il personale di supporto tecnico di IBM è apparso su tutte le maggiori BBS
e sistemi di conferenza, inclusi Compuserve, BIX, usenet e fidonet.
Molti altri programmi di supporto a OS/2 sono attivi. Il Developer Assistance Program aiuta gli
sviluppatori commerciali. IBM ha aperto una linea verde per materiale software e di supporto per
OS/2, e la IBM I. V. League supporta autori ed editori. L’IBM sponsorizza molte conferenze sugli
strumenti per OS/2 2.0, in cui gli sviluppatori possono avere le informazioni più aggiornate su OS/2
2.0 e sugli strumenti di sviluppo associati, e workshop di migrazione a OS/2, in cui gli sviluppatori
possono ottenere assistenza tecnica nella conversione delle loro applicazioni in OS/2 2.0.
In arrivo
Alcuni rivenditori hanno cominciato a sviluppare strumenti per OS/2 prima dell’uscita della GA.
Altri, notando l’enorme risposta all’uscita di OS/2 2.0, hanno annunciato prodotti o piani di rilascio
per OS/2 2.0 nell’ultima parte dell’anno. Le offerte attuali includono linguaggi, applicazioni CASE,
debugger e altro.
15
Nel campo dei linguaggi, Borland International, Clarion Software, MicroWay, Symantec e Watcom
hanno annunciato la realizzazione di compilatori C e C++. Watcom ha un compilatore FORTRAN a
32 bit, e Clarion ha annunciato le versioni per OS/2 2.0 dei suoi compilatori Pascal e Modula-2.
IBM apparentemente sta lavorando a un suo compilatore C++, mentre Arity vende un compilatore
Prolog a 32 bit per OS/2 2.0. Digitalk ha rilasciato una versione OS/2 2.0 del suo Smalltalk/V PM.
Micro Focus e Liant Software hanno annunciato compilatori COBOL per OS/2 2.0.
Caseworks, The Stirling Group, Guild Products, Guidance Technologies, Gpf Systems, VZ,
Vleermuis Software Research, Software Engineering International, Enfin Software, ImageSoft, ed
Intelligent Environments hanno annunciato nuovi tool CASE per OS/2 2.0 in PM. Se intendete
migrare applicazioni Windows in OS/2, cercate il Software Migration Kit di Micrografx.
Difficilmente vi serviranno altri strumenti di debugging al di là di quelli offerti da IBM. Comunque,
potreste considerare l’uso di Periscope debugger e di Soft & GUI’s Error Manager. SourceLine
software vende un browser ipertestuale per codice sorgente per OS/2 2.0. Personal Systems
Software vende un toolkit chiamabile dal C per scrivere device driver in OS/2 2.0. Intersolv ha
annunciato versioni per OS/2 2.0 di PVCS, PolyMake, PolyAwk e del Sage Professional Editor.
Hamilton Laboratories ha una versione a 32 bit della sua C shell. GammaTech ha una versione a 32
bit delle utilità HPFS, e Parallel PC’s sta vendendo una versione per OS/2 2.0 del Synetics SDK.
Per chi ha meno denaro a disposizione, per OS/2 2.0 sono disponibili molti strumenti di pubblico
dominio e shareware di buona qualità. Il compilatore GNU C/C++ per OS/2 2.0 è disponibile su
BIX, CompuServe e su altre BBS. È possibile scaricare anche molte utilità GNU (rcs, make, awk,
grep e altri) e GNU emacs.
La parola scritta
IBM ha annunciato la OS/2 Technical Library, un insieme di manuali per sviluppatori OS/2 al
prezzo di 295 dollari. I manuali sono indispensabili per chiunque programmi in OS/2. Uno sguardo
approfondito ai dettagli tecnici di OS/2 si può dare in The Design of OS/2, di H. M. Deitel ed M. S.
Kogan, pubblicato da Addison-Wesley (ISBN 0-201-54889-5).
Van Nostrand Reinhold ha pubblicato molti nuovi libri, inclusi Client-Server Programming with
OS/2 2.0 di Robert Orfali e Daniel Harkey (ISBN 0-44201219-5), Writing OS/2 2.0 Device Drivers
in C di Steve Mastrianni (ISBN 0442-01141-5), e OS/2 Presentation Manager GPI di Graham Winn
(ISBN 0-44200739-6). Van Nostrand offre inoltre una raccolta di sei libri a prezzo speciale.
Il tempo è giusto
L’interesse in OS/2 2.0 sta crescendo a grandi passi. Anche i suoi oppositori più decisi sono rimasti
impressionati dalle sue capacità. I fan del vecchio OS/2 1.x sono sempre qui, e sono stati raggiunti
da un gruppo di utenti e sviluppatori anche più ampio, e desideroso di toccare il supporto multitask
e multipiattaforma di OS/2 2.0.
La politica di prezzi aggressiva di IBM ha suggerito a molti utenti Windows di aggiornarsi e passare
a OS/2 2.0 a soli 49.95 $: gli utenti DOS possono fare l’aggiornamento a 99 $. L’interesse in OS/2
2.0 è provato anche dai molti messaggi nelle conferenze elettroniche pubbliche.
I tool per sviluppare il nuovo software a 32 bit per OS/2 sono arrivati? Farete meglio a crederci. È
ora di sviluppare. Certamente vorrete mantenere alcune delle vostre applicazioni DOS e Windows,
ma non potete ottenere la piena potenza di OS/2 senza le applicazioni specifiche a 32 bit. Al
crescere delle richieste degli utenti di PC, la potenza e la flessibilità di OS/2 diverranno sempre più
richieste e apprezzate. La possibilità di scaricare un file da linea telefonica mentre si aggiorna un
16
database, si stampano molti file e si modifica un documento dovrebbe convincere anche l’utente più
scettico del fatto che OS/2 2.0 rappresenta la miglior soluzione per le applicazioni software.
Informazioni sulle compagnie:
Arity Corp. (Prolog/32) (508) 371-1243
Borland International, Inc. (C++ for OS/2 2.0) (408) 438-8400
Caseworks, Inc. (CASE:PM 2.2, CASE:PM VIP (CUA 91)) (404) 399-6236
Clarion Software, Inc. (TopSpeed C, C++, Modula-2, Pascal) (305) 785-4555
Digitalk, Inc. (Smalltalk/V PM) (310) 645-1082
Enfin Software Corp. (Enfin/2) (619) 549-6606
GammaTech (GammaTech Utilities) (405) 359-1219
Gpf Systems, Inc. (Gpf 2.0) (203) 873-3300
Guidance Technologies, Inc. (Choreographer) (412) 231-1300
Guild Products, Inc. (Guild) (415) 593-3200
Hamilton Laboratories (C shell) (508) 358-5715
ImageSoft, Inc. (CommonView) (516) 767-2233
Intelligent Environments, Inc. (AM (Applications Manager)) (508) 640-1080
Intersolv (PVCS, PolyMake, PolyAwk) (503) 645-1150
Liant Software Corp. (RM/COBOL-85) (512) 343-1010
Micro Focus, Inc. (Micro Focus/2 COBOL) (415) 856-4161
Micrografx, Inc. (Software Migration Kit) (214) 234-1769
MicroWay, Inc. (NDP FORTRAN 386/486, C-C++ NDP 386/486) (508) 746-7341
Parallel PC's (Synectics SDK) (215) 670-1710
The Periscope Co., Inc. (Periscope/32) (404) 875-8080
Personal Systems Software, Inc. (C-Callable DevHlp Driver Library) (203)
242-8711
Soft & GUI, Inc. (Error Manager) (718) 769-8017
Software Engineering International (Primary Window Class) (407) 241-3428
17
SourceLine Software (SourceLink) (619) 587-4713
The Stirling Group (TbxShield, InstallShield) (708) 307-9197
Vleermuis Software Research (GUI Master) fax: 31-30-32-49-44
VZ Corp. (VZ Programmer) (801) 595-1352
Watcom (C 9.0/386, FORTRAN 77/386 9.0) (519) 886-3700
Confessioni di uno sviluppatore DDK
Il DDK di OS/2 non fa molta strada, ma è un inizio
In un freddo pomeriggio di febbraio, dopo lunga attesa, ricevetti una lettera dall’IBM che
annunciava il DDK della versione 2.0 di OS/2. Firmai immediatamente e rispedii il modulo di
risposta con un check di 15 $, e meno di una settimana dopo giocherellavo con l’OS/2 DDK. Avevo
sentito parlare del pacchetto per mesi. L’IBM aveva continuato a promettere di distribuirlo, ma
molti ritardi trascinarono le cose e la ditta fu costretta a chiedere agli sviluppatori di pazientare.
Fonti vicine a IBM dicevano che il problema non era dal punto di vista del codice: invece, nessuno
aveva la responsabilità di mettere assieme il codice e realizzare il DDK. Si parlò anche di problemi
legali con Microsoft. Alla fine, i problemi vennero risolti.
Un inizio dubbioso
Il DDK era contenuto in un CD-ROM, con i driver per Win-OS2 su due floppy disk. Ansioso di
capire cosa occupasse tutto quello spazio, iniziai a installarlo immediatamente su una macchina con
installata la beta 2.1 di dicembre. L’installazione chiedeva 50 MiB di spazio su disco. Avendone 52
liberi, pensai che andava tutto bene.
Sbagliato.
Circa al 95% dell’installazione, ottenni un errore di protezione generico e il temuto messaggio “The
system is stopped.” A giudicare dall’indirizzo dell’errore, pareva trattarsi di un device driver, e le
mie peggiori paure si realizzarono notando che le prime tracce del disco erano state riscritte. Il disco
non era più avviabile: la FAT era andata, insieme ai miei dati. Fortunatamente, si trattava di un
sistema privo di informazioni sensibili. Successivamente scoprii che si trattava di un bug della
versione beta 2.1: il codice dei device driver, come seppi dopo, richiede 54 MiB di spazio libero.
Riformattato l’hard disk, reinstallai il DOS e il codice beta della 2.1. Reinstallai poi anche la
maggior parte del software, facendo in modo da lasciare circa 100 MiB di spazio libero.
L’installazione andò a termine perfettamente, piazzando moltissimo codice sorgente sul disco. Tutti
i driver vennero installati: stampante, SCSI, display, driver virtuali e fisici. Il programma di
installazione permette di selezionare i driver da installare: aiuta a conservare spazio su disco, ma
trovare un driver particolare è un lavoro noioso.
Cosa c’è
Il DDK include il Microsoft CL386, un compilatore a 32 bit precedentemente assente, utilizzato per
creare il codice a 32 bit di OS/2 2.0. L’IBM ha usato CL386 anche per costruire tutti i VDD di OS/2
2.x. Nel pacchetto è inclusa anche una versione del MASM 5.1, pur se alcuni esempi richiedono la
versione 6.0. Questo mi ha disturbato: perché gli esempi non potevano richiedere un solo ambiente?
18
Anzi: perché in IBM si sviluppa per OS/2 con diversi compilatori e assemblatori? Gli sviluppatori,
così, sono costretti a usare il Microsoft C 5.1 e 6.0, CL386, il CSet/2 di IBM, e MASM 5.1 e 6.0. In
sua difesa l’IBM asserisce che intende correggere il problema in versioni successive del DDK.
Inoltre, nel DDK sono inclusi diversi strumenti, tra cui un programma che permette di cambiare
rapidamente gli attributi estesi, un programma per visualizzare la palette hardware, un programma
per controllare la stampante, TOUCH, SED, MAPSYM, LIB e il compilatore delle risorse RCPP.
Il DDK include in effetti dozzine di driver. Tra i driver di schermo, nel DDK sono compresi un
driver VGA a 16 bit, un driver 8514/A a 16 bit, un driver VGA a 32 bit, un driver Super VGA a 32
bit, e un driver indipendente dal dispositivo a 32 bit. Sono incluse anche le controparti virtuali. I
gestori base per VGA, 8514/A, CGA, EGA e XGA sono in cima alla lista dei driver. I floppy disk
contengono invece il codice sorgente di due driver video seamless basati su driver per Windows,
versioni 3.0 e 3.1.
Quanto alle stampanti, IBM fornisce solo due driver: un driver PostScript e un driver per plotter,
che pare essere codice OS/2 1.3. La cosa ha senso, dato che IBM continua a supportare tali driver su
OS/2 1.3. Il driver postscript va compilato con CL386. Il DDK inoltre include il codice sorgente del
driver per floppy, per ASPI (Advanced SCSI Programming Interface), IDE, SCSI PS/2, floppy
PS/2, e DASD PS/2. Per i driver per CD-ROM esistono i codici sorgente dei driver Hitachi,
Toshiba, NEC e Sony SCSI, oltre al CD-ROM Device Manager. Sono inclusi anche i driver per
mouse Mouse Systems, PC Mouse / Logitech, e VisiOn, anche se manca un driver virtuale.
A caccia
La prima sezione che ho esaminato è stata quella dei PDD, i miei driver preferiti. Sono da tempo un
fautore della scrittura di driver in C. Dispongo di una libreria di funzioni in C che permette a un
driver scritto in C di chiamare i device helper per OS/2 basati su registri, ed ero curioso di
conoscere l’offerta di IBM.
Nel CD sono inclusi una libreria con codice sorgente e make file, ma assolutamente nessuna
documentazione. Se siete bravi programmatori MASM probabilmente riuscirete a comprendere il
senso del codice assembly più-complesso-del-necessario e le macro, e il modo per chiamare le
funzioni leggendo il file include assembly, posto che ne abbiate il tempo.
Inoltre la libreria contiene molte chiamate a device helper non documentati. La libreria necessita di
documentazione, e che tale documentazione sia posta sul CD-ROM. Se le chiamate a funzione non
sono supportate, l’IBM farebbe bene a tenerne nota, in modo che gli sviluppatori non le usino nel
codice di produzione: nondimeno, le chiamate dovrebbero essere documentate. Sono stato contento
di vedere molto codice C. Dato che la maggior parte delle operazioni è veloce, non ha quasi senso
scriverle in assembly. I driver in C possono essere scritti in metà del tempo richiesto per usare
MASM, e sono più facili da correggere e supportare.
Non ho trovato il riferimento ai PDD, la bibbia degli scrittori di PDD. Né ho trovato i riferimenti ai
driver di presentazione e ai VDD. La cosa che confonde, di queste omissioni, è che IBM ha già
rilasciato tale documentazione nel Professional Developer’s Kit: non tutti i programmatori di driver,
peraltro, hanno bisogno del PDK, per cui praticamente vengono lasciati senza documentazione.
IBM intende produrre tale documentazione nelle versioni successive del DDK.
19
Cercare un senso nel DDK navigando attraverso oltre 50 MiB di codice è decisamente noioso. Il
CD-ROM dovrebbe includere le informazioni di navigazione necessarie in formato INF. Mi
piacerebbe anche avere il manuale di riferimento al Control Program, dato che la maggior parte
delle chiamate ai device driver viene fatta da queste API. Nel provare i miei driver, mi riferisco
sempre a tale guida, dato che trovo impossibile ricordare tutti i parametri e il loro ordine.
È notevole, inoltre, l’assenza di un esempio di driver di filesystem. Le informazioni sugli IFS di
OS/2 sono parziali e possono essere ottenute solo con un permesso speciale. Un breve scheletro di
IFS è ottenibile tramite CompuServe, ma l’IBM deve fare un lavoro migliore sugli IFS fornendo
codice di esempio e documentazione come parte standard del DDK. Non esiste alcuna scusa per non
fornire il codice.
Altri miglioramenti
Il DDK è un buon punto di partenza, ma è molto difficile usarlo senza buona documentazione. Dato
che IBM ha promesso di rilasciarne aggiornamenti regolarmente, dovrebbe avere il tempo di
migliorare il prodotto. Molti esempi contengono centinaia di linee di codice senza documentazione,
e alcuni non dicono nemmeno quel che fa l’esempio. IBM deve fornire maggiore documentazione
per permettere agli sviluppatori di capire cosa c’è sul CD e come trovarlo. Nessuno ha tempo di
navigare un intero CD cercando qualcosa che potrebbe anche non esserci.
Sarebbe bene inserire un debugger come ASDT32. Il DDK manca anche di esempi su driver a
carattere, come driver per porta seriale o parallela, driver di acquisizione dati o memory mapped.
Questi “altri” driver riguardano più di metà dei dispositivi che richiederanno driver per OS/2. Il
DDK dovrebbe anche includere un tutorial per scrivere device driver con codice di esempio. Tutti
questi documenti, inclusa la guida di riferimento ai device driver, dovrebbero essere presenti in
diversi formati (INF, Read/2, ASCII e PostScript).
IBM deve fare un lavoro migliore nel supporto ai programmatori di device driver, specie per il fatto
che questo è il tradizionale tallone d’Achille di OS/2. Attualmente IBM ha attiva una piccola BBS
in cui i programmatori possono fare domande e scaricare codice di esempio. Si tratta però di un
sistema single user, che manca della capacità di thread di messaggi, come si vede in CompuServe o
BIX. Periodicamente, domande e risposte vengono fuse in un file scaricabile, ma la cosa non ha la
comodità di un thread di messaggi su CompuServe. L’IBM ha annunciato che CompuServe è il
forum ufficiale di supporto pubblico a OS/2 2.x. Quindi, IBM dovrebbe supportare i device driver
nel forum lì presente, dove un uditorio di sviluppatori molto più ampio potrà beneficiare del traffico
di messaggi.
Una sveglia?
Pare che IBM abbia recepito il messaggio. È stato creato un gruppo speciale interno alla compagnia,
per promuovere i device driver in OS/2 pubblicamente e internamente. Il DDK è diventato un
prodotto, con un manager assegnato. IBM ha pianificato rilasci trimestrali del DDK, di cui ogni
versione dovrebbe migliorare. I rilasci futuri includeranno driver e strumenti per: pen computing,
multimedia, XGA, 8514/A, SCSI, mouse, tastiere, IFS, porte seriali e parallele, schermi sensibili al
tatto e PCMCIA, oltre a un’ampia scelta di strumenti e documentazione online.
Questo mese, IBM terrà una conferenza di tre giorni sul DDK di OS/2. Gli sviluppatori di driver dei
laboratori di Boca Raton saranno presenti per conversare e incontrarsi con gli sviluppatori.
Probabilmente il rilascio del DDK e la conferenza segneranno un punto di svolta per lo sviluppo di
device driver per OS/2.
20
OS/2 ha supporto ai device
IBM finalmente semplifica il supporto di OS/2 ai rivenditori
Forse, il maggior miglioramento di OS/2 2.1 rispetto alla 2.0 è il supporto enormemente migliorato
all’hardware di terzi. Per esempio, per la prima volta in assoluto sono in grado di usare la mia ATI
Graphics Ultra Plus a 1280x1024 con 256 colori. Gli utenti di schede video basate su chip S3
riportano successi similari. Questi driver sono ancora in beta release, ma diverranno presto di
dominio pubblico.
Al momento in cui leggete, OS/2 ha quasi sicuramente il supporto per la massima parte dei chipset
acceleratori video provenienti da compagnie come S3, ATI e Weitek. Sarà presente anche il
supporto a CD-ROM non SCSI, stampanti a colori a 24 bit, scanner, schermi al tatto e un gruppo di
dispositivi di I/O.
La situazione è quantomeno inusuale per OS/2. Per la maggior parte della sua esistenza, il sistema
non ha avuto supporto significativo per hardware di terzi. La maggior ragione di questa carenza è la
mancanza di un DDK di IBM. I driver per OS/2 sono difficili da scrivere anche nel migliore dei
casi: senza esempi e documentazione, quasi impossibili.
Storia dei driver
Al rilascio di OS/2 2.0, l’unico toolkit per device driver per OS/2 era quello di Microsoft, già
ritirato dal mercato, peraltro. Molte cose modificate in OS/2, inoltre, impedivano di usare il
Microsoft DDK con la nuova versione. Il kernel debugger, specifico della versione, non poteva
funzionare con OS/2 2.0. Inoltre, l’architettura LADDR (LAyered Device DRiver) di Microsoft è
stata sostituita dall’architettura ADD (Adapter Device Driver) di IBM, e l’interfaccia Microsoft
Strategy 2 per dischi rimpiazzata con la tecnologia IBM EDDI (Extended Disk Driver Interface).
Anche i cambiamenti nella compatibilità DOS hanno avuto impatto sui device driver. OS/2 1.x
supportava solo una sessione DOS, in cui il processore lavorava in modo reale. OS/2 2.0 ha
introdotto le Virtual DOS Machine, che consentono a un programma DOS di lavorare nel suo
spazio di memoria di 1 MiB, completamente ignaro di altre applicazioni in esecuzione. Queste
applicazioni hanno bisogno di accesso condiviso all’I/O anche se suppongono di avere un accesso
di tipo esclusivo.
La soluzione implementata da IBM è quella dei VDD, Virtual Device Driver, che arbitrano la
titolarità del dispositivo. I VDD hanno virtualizzato anche i dispositivi comuni, come clock, porte
seriali e controller DMA.
I VDD sono DLL a 32 bit, che determinano un altro problema: non esistevano compilatori a 32 bit
per OS/2 al rilascio di OS/2 2.0. In IBM era stato usato un compilatore Microsoft, che non era
possibile distribuire all’esterno. Come risultato, gli sviluppatori erano costretti a scrivere i VDD in
assembly, o non scriverli affatto.
La svolta
Il risultato di questi problemi era la fondamentale inesistenza di driver per OS/2 2.0. Nell’ultimo
periodo del 1992, all’IBM capirono finalmente che la mancanza di driver stava limitando il
successo di OS/2 2.0 e avrebbe danneggiato l’atteso successo di OS/2 2.1.
Venne formato a Boca Raton un gruppo diretto da Lois Dimpfel, direttore della divisione Personal
Operating Systems di IBM, per rendere realtà il supporto a dispositivi avanzati. Il gruppo si
21
compose di esperti nei device driver provenienti da ogni parte di IBM. Citato anche come
Worldwide Industry Hardware Support department, il gruppo aveva una missione semplice: fare
tutto ciò che fosse necessario per rendere realtà il supporto ai dispositivi avanzati da parte di OS/2.
Il culmine di questo lavoro fu il rilascio della prima beta del DDK, nei primi periodi del ’93. Per
assicurarsi di ricevere risposta da un ampio numero di sviluppatori, la beta venne rilasciata a una
cifra nominale di 15$. Il DDK era disponibile solo su CD-ROM, data la dimensione e la
complessità, e includeva il compilatore C precedentemente non rilasciato, per poter finalmente
scrivere VDD anche in C.
Il primo DDK commerciale venne rilasciato pochi mesi dopo. Chiamato IBM Device Driver
Sourcekit 1.0, il DDK conteneva più di 70 sorgenti di device driver, oltre a un ampio insieme di
documentazione in linea. Erano inclusi codici sorgente per driver per mouse, dischi, CD-ROM,
seriali, parallele, e floppy disk, oltre a una modesta collezione di strumenti di prova.
Nel luglio di quest’anno, l’IBM ha tenuto la prima conferenza sui device driver. La conferenza fu
tenuta da oltre 300 sviluppatori di driver per OS/2. Oggi, stanno quantomeno cominciando ad
apparire driver per tutti i tipi di dispositivo, non solo per gli sforzi del team di Boca Raton, ma
anche per il successo di OS/2 2.1. L’IBM ha riportato il supporto ai dispositivi in OS/2 sul binario
giusto, finalmente.
Scrivere device driver – come ottenere il meglio da OS/2
IBM Developer Connection News, volume 4
OS/2 fornisce molti driver di base per ottenere informazioni sulla configurazione di sistema. I più
popolari sono TESTCFG e OEMHLP.
OEMHLP è stato pensato per dare informazioni a programmi e driver relative alla configurazione
di sistema, abilitando driver e applicazioni a configurarsi in base all’hardware disponibile. Tali
funzioni sono state documentate da poco nel Sourcekit, e sono degne di nota.
OEMHLP può ottenere informazioni come l’adattamento OEM, le informazioni sulla macchina, il
chipset video, i font video, le informazioni EISA e BIOS ROM, la memoria e molte altre
informazioni sensibili.
TESTCFG fornisce ulteriori capacità, permettendo di ottenere il tipo di bus (EISA, ISA,
MicroChannel), ottenere i contenuti dei registri Micro Channel Programmable Option Select (POS),
l’EISA card ID, l’I/O diretto a registri (senza bisogno di un segmento IOPL), e ottenere una copia
della memoria non di sistema (quella tra 640 kiB e 1 MiB).
Se siete interessati a questi driver di base, o ai device driver in genere, consultate il DDK! L’ultimo
DDK, versione 1.2, contiene il sorgente di TESTCFG. Consultate la documentazione del DDK e il
codice sorgente per le ultime informazioni. Usando questi strumenti potete risparmiare molto tempo
nello sviluppo di driver.
L’indice di questa newsletter contiene informazioni sul DDK.
22
Suggerimenti per i device driver
1: installare una piccola partizione avviabile da usare nello sviluppo di un device driver su una sola
macchina.
Tecnica: dato che per il test sono necessari molti cicli di avviamento, installate una piccola
partizione avviabile che contenga una linea che punta al driver in oggetto. Non sono necessari
cambiamenti al file CONFIG.SYS tra l’avvio nell’ambiente di prova e quello di sviluppo. E non vi è
rischio di compromettere l’ambiente primario.
2: installando un handler del timer nel driver, non chiamate le funzioni SetTimer o TickCount fino
alla fine della sezione Init, prima di ritornare al kernel. I gestori del timer solitamente si riferiscono
a elementi dati relativi a DS, che divengono deriferiti quando il driver fallisce e restituisce 0 come
offset codice e dati. Accade che il gestore è ancora nell’elenco dei gestori di timer da chiamare, e
viene chiamato dopo il fallimento ma prima della rimozione del gestore, causando così un errore di
protezione generale. Un’alternativa è di chiamare sempre ResetTimer in caso di errore, prima di
restituire il valore 0 come offset per codice e dati, oppure rendere ultima nella sezione Init la
chiamata a SetTimer/TickCount.
3: per impostare un breakpoint in un particolare punto del driver, inserite un’istruzione INT 3 in
assembly, oppure scrivete una funzione assembly che chiami INT 3 e sia chiamabile dal C. Inserite
la chiamata nel sorgente del driver, ricompilate e riavviate. Assicuratevi che sia installato il kernel
debugger, o il sistema non si avvierà.
4: progettando il driver, lavorate per cercare di avere un approccio “a piani”. Tentate di separare le
parti specificamente software del driver dalla porzione hardware, similmente al modello ADD.
Questo approccio a livelli separati permette di ottenere molte porzioni di codice riutilizzabile, utile
per ulteriori scopi.
Scrivere device driver: da dove partire?
IBM Developer Connection News, volume 2
Ricevo molte richieste di sviluppatori che vorrebbero iniziare a scrivere device driver. Una delle più
comuni è “Come inizio a scrivere un device driver per OS/2?” Dipende dalle vostre conoscenze.
Dato che i driver interagiscono con il kernel di OS/2, bisogna avere una buona comprensione delle
funzioni base di OS/2, come il multithreading, le priorità, l’allocazione di memoria e l’
indirizzamento dei dati.
Molte delle domande che ricevo riguardano un fraintendimento su come funzionano i vari tipi di
indirizzi. I programmatori di driver devono poter lavorare con indirizzi virtuali, lineari, fisici e reali.
Dato che il device driver interagisce con il processore al livello della macchina, è impagabile una
buona conoscenza dell’architettura del processore. Un errore in un device driver di solito blocca il
sistema, e rintracciarlo può essere molto noioso e faticoso senza saper dove guardare.
Se scrivete in C, vi serve un compilatore a 16 bit come il Microsoft C versione 5.1 o 6.0. Servirà
anche un assembler, come il Microsoft Macro Assembler versione 6: versioni precedenti, come la
5.1, sono comunque utilizzabili.
23
Se intendete scrivere device driver virtuali (VDD), servirà un compilatore a 32 bit, come l’IBM C
Set/2 o C Set ++ (raccomandato), oppure il compilatore CL386 a 32 bit incluso nel DDK. Il DDK
include anche il kernel debugger e ASDT32, necessario per il debug dei device driver. Sul mercato
è disponibile il debugger della Periscope.
Dovreste prendere anche l’OS/2 Technical Library, una collezione di 25 kg di libri di riferimento
sullo sviluppo, che include la OS/2 Physical Device Driver Reference, la Virtual Device Driver
Reference e la Presentation Driver reference. La libreria è anche parte della OS/2 Online Book
Collection su CD-ROM.
Il libro “Wrinting OS/2 2.1 device drivers in C” è disponibile. È la sola guida scritta per i driver di
OS/2 2.x. Chiamate l’1-800-842-3636 per ordinare.
Il supporto per sviluppatori è gratuito tramite l’IBM DUDE (Dynamic Upload and Download
Environment). Periodicamente il team DUDE archivia le domande (rimuovendo i nomi) e le
inserisce in un singolo file scaricabile. Per accedere al DUDE, vedete l’indice della newsletter.
Ho sempre usato il C per i miei driver; l’IBM, storicamente, li ha scritti sempre in Assembler. Ciò si
evidenzia nel codice della guida ai PDD, che contiene esempi in MASM delle chiamate DevHlp.
Con l’avvento di Workplace OS, IBM ha iniziato a documentare le interfacce C a DevHlp. Versioni
future del DDK e delle guide includeranno tali interfacce.
Altra domanda che mi si rivolge spesso è: “Come posso inizializzare un adattatore mappato in
memoria durante l’inizializzazione (Init)?” Spesso gli adattatori devono caricare la loro memoria
con un programma, oppure devono inizializzarla. Molti programmatori di driver sperimentano il
loro primo Trap 13 (General Protection Fault) tentando di eseguire l’operazione nella sezione Init.
La causa più comune è che il programmatore di driver alle volte dimentica che Init è una fase
lanciata a livello 3. Mappare un indirizzo fisico in un indirizzo virtuale con PhysToVirt genera un
puntatore basato sulla GDT, non utilizzabile da un thread a livello 3, che non ha accesso alla GDT
ma soltanto alla LDT.
La soluzione al problema è di mappare l’indirizzo fisico dell’adattatore a un indirizzo virtuale
mappato nella LDT dell’applicazione. Ciò si fa con la chiamata DevHlp PhysToUVirt, invece che
con PhysToVirt.
Se dovete immettere molti dati nell’adattatore, manteneteli in un file su disco e usate le funzioni
della API DosOpen, DosRead e DosClose. Ciò è possibile perché Init lavora a livello 3, lo stesso
della massima parte delle applicazioni.
“Come fa un driver a trasferire dati a tempo di interrupt?” è una domanda complessa, ma semplice
una volta che le cose sono spiegate chiaramente. Ricordate che Init lavora come thread a livello 3,
con accesso alla LDT dell’applicazione. Il resto del tempo viene passato dal driver a livello 0, il più
basso del sistema: il driver ha accesso completo alla GDT e all’intero sistema.
Quando si ha un interrupt, però, il programma che vi serve potrebbe non essere quello in
esecuzione. Per esempio, potrebbe essere bloccato in attesa di I/O o di un semaforo. Il contesto,
ossia l’ambiente corrente a quell’istante, è impredicibile.
Tentare di mappare un indirizzo di un buffer dell’applicazione all’interrupt non funziona. Per
mantenere l’indirizzabilità in qualsiasi contesto, l’indirizzo del buffer dell’applicazione va mappato
24
verso un selettore della GDT. Comunque, il selettore va allocato durante Init e quindi mappato al
selettore GDT per tempi successivi. Ricordate però che anche se potete mappare il selettore durante
Init, non potete comunque usarlo.
Scrivere device driver – una breve occhiata a OS/2 SMP
IBM Developer Connection News, volume 5
L’architettura SMP di OS/2 è decisamente semplice. Solo una copia di OS/2 può girare a uno stesso
momento, non importa quanti processori siano presenti, per cui non vi è bisogno di sincronizzare
più copie del sistema operativo. L’accesso al sistema viene sincronizzato e serializzato tramite gli
spinlock dei processori.
Uno spinlock altro non è se non una breve sezione di codice, eseguito in un ciclo stretto fino a che
una variabile viene azzerata. Se avete mai avuto un bug nel vostro driver per OS/2 in cui il codice
andava in loop a ring 0, sapete che cosa sia uno spinlock. Non è possibile interrompere tale ciclo
con il kernel debugger, e di solito siete costretti a spegnere. Con OS/2 SMP si ha la stessa cosa.
OS/2 SMP fornisce un’astrazione dall’hardware tramite i Platform Specific Driver, o PSD. Come
un device driver scherma l’applicazione dalle specifiche del dispositivo, il PSD isola OS/2 kernel
dallo specifico hardware del processore. Per fornire questo livello di astrazione, il PSD esporta
funzioni generiche chiamate dal kernel. Tali funzioni vengono tradotte dal PSD in operazioni
specifiche della particolare piattaforma.
I PSD sono device driver speciali a modello piatto, DLL a 32 bit caricate con DEVICE nel file
Come gli ADD di OS/2, devono conformarsi al modello 8.3 per i nomi di file, e il
nome non deve contenere informazioni su unità o percorso.
CONFIG.SYS.
OS/2 SMP richiede un PSD per l’inizializzazione del sistema. Il sistema stamperà un errore se non
troverà un PSD valido per la piattaforma. Se un passaggio non viene completato correttamente,
l’inizializzazione del sistema viene fermata, e viene stampato un messaggio d’errore.
Seguono le DevHlp introdotte con OS/2 SMP:
Funzione DevHlp Codice Descrizione
CreateSpinLock
0x6f
Crea uno spinlock di sottosistema
FreeSpinLock
0x70
Elimina uno spinlock di sottosistema
AcquireSpinLock
0x71
Acquisisce uno spinlock
ReleaseSpinLock
0x72
Rilascia uno spinlock
PortIO
0x76
Processor-independent port I/O
SetIRQMask
0x77
Setta/Resetta una maschera IRQ
GetIRQMask
0x78
Legge lo stato della maschera IRQ corrente
È stata introdotta un’ultima DevHlp virtuale, chiamata VDHPortIO.
OS/2 SMP fornisce un insieme di API per l’ambiente SMP. Segue un elenco delle API e delle
funzioni corrispondenti. Queste informazioni sono soggette a cambiamenti.
25
API
Funzione
DosCreatSpinLock
Crea uno spinlock di sottosistema
DosFreeSpinLock
Libera uno spinlock di sottosistema
DosAcquireSpinLock
Acquisisce uno spinlock di sottosistema
DosReleaseSpinLock
Rilascia uno spinlock di sottosistema
DosAllocThreadLocalMemory
Alloca memoria per un thread
DosFreeThreadLocalMemory
Libera la memoria allocata per un thread
DosQuerySysInfo (changed)
Restituisce informazioni sul sistema
Altre informazioni sulla scrittura di device driver per OS/2 SMP sono presenti nell’OS/2 for SMP
CD-ROM e nella terza edizione di “Writing OS/2 2.x device drivers in C”, prevista per l’ultima
parte di quest’anno.
SUGGERIMENTO: state scrivendo un driver per OS/2? Preparatevi a OS/2 for PowerPC
strutturando il driver su più piani, come nel modello ADD, separando il piano hardware da quello
software. Questo vi ridurrà il lavoro quando porterete i driver in OS/2 PPC.
SUGGERIMENTO: state scrivendo ancora i driver in Assembly? Provate a scriverli in C. La
direzione corrente nei sistemi operativi è di rendere i device driver indipendenti dall’hardware, e
farli divenire applicazioni piuttosto che lavori del kernel. Scrivendo il driver in un linguaggio ad
alto livello potrete portare i driver verso sistemi come OS/2 PPC più facilmente.
SUGGERIMENTO: stampate un’intestazione di device driver dal kernel debugger. L’intestazione è
sempre all’inizio del primo segmento dati del device driver. L’intestazione contiene il nome del
dispositivo, i selettori di segmento codice e dati, l’indirizzo dell’entry point IDC e un puntatore alla
prossima intestazione di driver.
TECNICA: per mostrare l’intestazione del device driver, inserite “.d dev ds:0” al prompt dei
comandi del kernel debugger. Questi mostrerà l’intestazione del driver con stringhe di testo che
definiscono i campi, per esempio:
##.d dev ds:0
DevNext: 06f0:ffff
DevAttr: 8940
DevStrat:0000
DevInt: 0000
DevName: TESTCFG$
DevProtCS: 06f8
DevProtDS: 06f0
DevRealCS: 0000
DevRealDS: 0000
26
Scrivere device driver – Interrupt
IBM Developer Connection News, volume 3
Molte volte mi si chiede “Cos’è il sistema di interrupt e come faccio a programmare per esso?” Per
rispondere alla domanda, l’articolo fornisce una vista iniziale al sistema di interrupt.
Nell’architettura tradizionale Intel, il sistema di interruzione è un gruppo di elettroniche che
permettono a eventi esterni di interrompere il programma in esecuzione. Uno dei componenti, il
Programmable Interrupt Controller (PIC) viene informato dell’evento esterno, e a suo turno
informa la CPU che è stato ricevuto un interrupt. La CPU salva l’indirizzo del codice in esecuzione
e chiama un indirizzo speciale assegnato a quel livello di interruzione (IRQ), per eseguire
l’elaborazione dell’evento. Quando l’interrupt è stato servito, la CPU riprende l’esecuzione del
codice originale all’istruzione successiva. Questa è ovviamente solo la situazione base.
Attualmente, la maggior parte dei PC ha 15 diversi livelli di interrupt, dati da due PIC a 8 canali.
Uno degli IRQ, il 2, non è disponibile, perché serve a mettere in cascata i due PIC. I PC come
l’IBM XT hanno solo un PIC; pertanto essi erano limitati al numero di adattatori installabili. Con
sistemi più grandi, anche 15 livelli di IRQ sono pochi. Il bus ISA usa interrupt edge triggered, una
limitazione che impedisce la condivisione degli interrupt. Le macchine Microchannel ed EISA
usano interrupt di tipo level triggered, che permettono agli adattatori di condividere i livelli di
interrupt. OS/2 attualmente supporta fino a quattro adattatori Microchannel o EISA per ogni IRQ.
In DOS, si possono agganciare gli interrupt sostituendo un puntatore nella tabella degli interrupt
nella pagina base. In OS/2, comunque, bisogna registrarsi per la notifica di interruzione chiamando
la routine Device Helper SetIRQ. Uno dei parametri di SetIRQ è l’indirizzo del gestore di interrupt
del driver. Il kernel di OS/2 memorizza gli interrupt e notifica il driver della ricezione di un
interrupt chiamando il gestore da voi scritto.
Il gestore delle interruzioni deve fare le sue operazioni rapidamente e rientrare riabilitando le
interruzioni. L’ultima cosa da fare prima di uscire dal gestore delle interruzioni è chiamare la
funzione DevHlp EOI, che resetta la logica delle interruzioni consentendone il passaggio. In caso
contrario, il driver non riceverà ulteriori interrupt. Se lasciate le interruzioni disabilitate all’uscita
del gestore, sarà il kernel di OS/2 a riattivarle.
Cercate sempre di eliminare o limitare l’annidamento di interrupt. Questo sarà necessario se il
driver riceve un interrupt mentre sta già elaborando un interrupt nel gestore. La logica di
annodamento può essere complicata e difficile da correggere. Il gestore di interrupt non dovrebbe
mai annidare più di due interruzioni. Usate cautela nel progettare gestori di interruzione per
Microchannel ed EISA, perché il gestore viene accessato con le interruzioni abilitate.
Una domanda che mi rivolgono spesso i principianti è “Nel debug del mio device driver ho notato
che il driver riceve un IOCtl packet, category 5, funzione 0x48. La mia applicazione non ha mai
lanciato tale pacchetto: da dove viene, e cosa significa?”. Il kernel lancia tale IOCtl con lo scopo di
capire se il driver è un driver di stampante. Se non lo è potete semplicemente restituire
ERROR_BAD_COMMAND, oppure ignorarlo restituendo Done.
SUGGERIMENTO: per lo sviluppo di driver, usate una macchina con partizioni FAT. Il crash di
una macchina HPFS nello sviluppo di driver può portare a danni di file e riavvi lunghi.
27
SUGGERIMENTO: invece di usare un terminale ASCII per il debug, usate un vecchio PC con un
programma di comunicazione, come Procomm o Telix. Ciò permette di abilitare la cattura di dati e
tenere un diario delle sessioni di debug. Mantenete una registrazione dei contenuti di tutti i registri
ponendo breakpoint alle locazioni desiderate, e cambiate il comando predefinito del kernel
debugger in r;g. La ”r” fa in modo di stampare tutti i registri, e la “g” continua l’esecuzione.
SUGGERIMENTO: piazzate la sezione Init alla fine del codice. Restituite un puntatore all’inizio
della sezione Init quando siete alla fine della sezione. Questo rilascia la memoria occupata da Init.
Non cercate di risparmiare spazio o tempo durante Init, nessuno dei due è importante, dato che Init
viene chiamata solo all’avvio del sistema e la memoria può essere restituita a OS/2 alla fine di Init.
Scrivere device driver – basi del Plug & Play
IBM Developer Connection News, volume 6
I cambiamenti hardware necessari per supportare il Plug & Play da parte di un adattatore sono
relativamente minimi, le parti costano in totale meno di 5$ per adattatore. L’hardware attuale
consiste di uno shift register a 72 bit, un po’ di memoria non volatile e alcuni buffer a tre stati. Il
registro viene usato per identificare l’adattatore. Esso contiene 32 bit di dati del produttore, 32 bit di
numero seriale e un checksum a 8 bit. Gli adattatori PnP contengono inoltre alcuni byte di
configurazione salvati in una RAM non volatile. Tali dati possono essere letti e cambiati dopo aver
posto la scheda PnP in stato di configurazione.
Uno speciale insieme di registri su ogni adattatore PnP, contenuto in un chip ASIC, fornisce accesso
alle schede PnP e ai dati delle risorse. Anche se il bus ISA non è indirizzabile, il chipset PnP
fornisce un metodo per isolare le schede PnP sul bus, una per volta, fino a che tutte le schede non
sono state identificate. A tale scopo si pone la scheda adattatore in un modo a bassa o alta
impedenza, dipendendo dai dati scritti nella scheda e dai contenuti del registro LFSR
sull’adattatore. Le schede vengono isolate, selezionate e configurate, quindi poste in alta
impedenza. Questo permette a una singola scheda di venire selezionata e configurata, quindi l’alta
impedenza impedisce alla scheda di riprendere parte al processo di selezione isolata.
Due porte specifiche di I/O sono state riservate per le operazioni PnP. La prima è 0x279,
attualmente porta di stato della stampante, che di norma è un registro a sola lettura. La seconda è
0x7A9, porta di stato della stampante più 0x800. Queste due porte danno accessibilità all’hardware
PnP su ogni scheda usando una speciale sequenza software di letture e scritture a queste porte. Si
usa una terza porta per leggere dati e scriverne alla scheda. Questa terza porta, invece, è rilocabile
da parte del software di isolamento PnP. Le specifiche PnP definiscono l’inizio di questa porta a
0x203, ma in pratica sarebbe bene iniziare a 0x20B, dato che un joystick standard occupa gli
indirizzi più bassi della porta. Tutto l’I/O a registri PnP viene realizzato con trasferimenti a 8 bit; i
trasferimenti a 16 bit non sono supportati.
Il vero e proprio supporto PnP richiede modifiche al BIOS per rilevare e configurare le schede PnP
a tempo di avviamento. Le nuove macchine, dotate di memorie Flash, sono semplici da modificare;
macchine meno recenti possono creare maggiori difficoltà. All’avviamento, il BIOS ordina la
sequenza di isolamento per assicurare che le schede necessarie all’avviamento siano attivate. Si
suppone che tali schede restino sempre attive. Nella maggior parte dei casi la cosa si fa tramite un
cavalletto o un commutatore sulla scheda. Le schede in stato attivo non prendono parte alla
sequenza di isolamento: se inserite una nuova scheda, al prossimo avviamento il BIOS dovrebbe
avviare una sequenza di auto-configurazione che abilita la nuova scheda con impostazioni valide.
28
Scrivere device driver – segmenti multipli
IBM Developer Connection News, volume 7
Occasionalmente, mi è necessario scrivere driver che occupano più segmenti codice o dati. Se
seguirete le linee presentate qui, potrete evitare alcune ore di sviluppi frustranti.
Il cambiamento maggiore consiste nel rendere FAR tutti i puntatori e le funzioni. Ciò permette di
riferire funzioni e dati oltre i limiti di segmento dei 64kiB, e richiede l’uso di chiamate FAR alle
librerie DevHlp. Allo scopo potete modificare la libreria DHCalls presente nel Device Driver Kit
della Developer Connection per OS/2, oppure, se state usando la DevHlp Library di Personal
Systems Software, chiamare il numero (203) 693-0404 per aggiornamenti.
Una volta convertite le chiamate in procedure FAR, assicuratevi di aver rimosso ogni riferimento
NEAR nel codice assembly e C. È poi necessario cambiare i parametri del compilatore per usare le
chiamate FAR, come segue:
cl -c -Alfu -Gs /NT_TEXT -G2 -Zl -Zp yourfile.c
Il parametro /NT_TEXT nomina il segmento e consente di ordinare i segmenti a tempo di link.
Quando il primo segmento codice si avvicina al limite dei 64 kiB, potete nominare nuovi segmenti
codice con nomi diversi (per esempio /NT_TEXT2), cosa che vi permetterà di piazzare il codice in
un nuovo segmento usando il codice di avvio assembly e il file .DEF. Ovviamente, è necessario il
link con OS2286.LIB e il modello di memoria Large LLIBCEP.LIB, al posto di SLIPCEP.LIB.
.SEQ
_DATA
_DATA
segment
ends
word public 'DATA'
CONST
CONST
segment
ends
word public 'CONST'
_BSS
_BSS
segment
ends
word public 'BSS'
FAR_BSS
FAR_BSS
segment
ends
word public 'FAR_BSS'
DGROUP
group FAR_BSS, CONST, _BSS, DATA, _DATA
_TEXT
segment
assume
.286P
;
_STRATEGY
__acrtused:
;
.
.
ret
;
_STRATEGY
proc
word public 'CODE'
cs:_TEXT, ds:DGROUP, es:NOTHING, ss:NOTHING
far
; to satisfy EXTRN in C-generated modules
endp
_INT_HANDLER proc far
_INT_HANDLER endp
29
_TIM_HANDLER proc far
_TIM_HANDLER endp
_TEXT
ends
_TEXT2
_TEXT2
segment word public 'CODE'
ends
RMCode
RMCode
segment word public 'CODE'
ends
;
stick RM code in second segment
CGROUP group _TEXT2,RMCode
end
Codice di esempio 1. Codice di avviamento
Successivamente, bisogna inizializzare tutte le variabili globali. In caso contrario, il linker tenterà di
porle in un secondo segmento dati posto davanti al primo segmento codice. Inizializzare le variabili
forza il linker a porle nel segmento dati predefinito. Una volta fatti i cambiamenti, compilate e
collegate il device driver ed esaminate il file mappa per assicurarvi che tutte le variabili siano state
inizializzate e risiedano nel segmento dati predefinito.
LIBRARY YOURLIB
PROTMODE
SEGMENTS
_DATA
CLASS'DATA'
CONST
CLASS'CONST'
_BSS
CLASS'BSS'
FAR_BSS
CLASS'FAR_BSS'
_TEXT
CLASS'CODE'
_TEXT2
CLASS'CODE'
RMCode
CLASS'CODE'
PRELOAD
PRELOAD
PRELOAD
PRELOAD
PRELOAD
PRELOAD IOPL
PRELOAD IOPL
Codice di esempio 2. Il file .DEF per il segmento codice extra
Per ordinare correttamente i segmenti, e marcare quelli extra come IOPL, è necessario modificare il
file .DEF. Ciò terrà i segmenti in giro abbastanza a lungo da bloccarli, realizzando il passo finale.
Nella sezione Init del driver, acquisite il selettore di ogni funzione che risieda nel segmento
superiore, e chiamate la routine DevHlp Lock con quel selettore. Assicuratevi di usare un lock a
lungo termine.
fptr
= (PFUNCTION) SomeFunction;
codesel = SELECTOROF(fptr);
// lock the second code segment down permanently
if(LockSeg(
codesel,
// selettore
30
1,
// lock a lungo termine
0,
// attende il lock segmento
(PLHANDLE) &lock_seg_han_code)) // handle restituito
return (RPDONE | RPERR | ERROR_GEN_FAILURE);
return (RPDONE);
Codice di esempio 3. Bloccaggio del segmento codice extra.
L’intestazione del driver richiede offset alle routine di strategia e IDC, per cui le dichiarerete NEAR
prima dell’intestazione oppure userete un altro metodo a scelta.
Serve ancora spazio per quei buffer particolarmente ampi? Nessun problema. Usate l’opzione del
compilatore /ND per rinominare i segmenti dati (per esempio, DATA2). Prendete il nome di una
variabile nel segmento dati alto, acquisite un puntatore a essa, estraete il selettore e chiamate
LockSeg con quel selettore:
cl -c -Alfu -Gs /NT_TEXT /ND_DATA2 -G2 -Zl -Zp yourfile.c
LIBRARY YOURLIB
PROTMODE
SEGMENTS
_DATA
CLASS'DATA'
CONST
CLASS'CONST'
_BSS
CLASS'BSS'
FAR_BSS
CLASS'FAR_BSS'
_TEXT
CLASS'CODE'
_TEXT2
CLASS'CODE'
RMCode
CLASS'CODE'
_DATA2
CLASS'DATA'
PRELOAD
PRELOAD
PRELOAD
PRELOAD
PRELOAD
PRELOAD IOPL
PRELOAD IOPL
PRELOAD IOPL
Codice di esempio 4. Il file .DEF per il segmento dati extra.
Finito. Beh, quasi: ricordate di marcare come IOPL il segmento dati aggiunto.
Ricordate di usare saggiamente lo spazio aggiunto!
Nota: la maggior parte dei miei driver viene scritta usando il modello di memoria small con il
Microsoft C 6.0.
31