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