|
Appunti informatica |
|
Visite: 1978 | Gradito: | [ Grande appunti ] |
Leggi anche appunti:Cast e conversioni di tipoCast e conversioni di tipo In una espressione è sempre possibile avere operandi Operatori aritmeticiOperatori aritmetici Gli operatori aritmetici del C sono i simboli di addizione Allocazione dinamica della memoriaAllocazione dinamica della memoria Quando è dichiarata una variabile, il compilatore |
Sempre più difficile: dopo avere affrontato i TSR (pag. ) è ora il turno dei device driver. Di che si tratta? Un device driver è, come evidenzia il nome stesso, un pilota di una qualche diavoleria: insomma, un programma dedicato alla gestione di una periferica hardware.
Dal punto di vista logico i device driver sono estensioni del DOS, che li carica durante la fase di bootstrap: si tratta di un meccanismo che consente, normalmente, di personalizzare la configurazione software del personal computer incorporandovi a basso livello le routine necessarie per pilotare in modo opportuno le periferiche aggiuntive hardware (scanner, scheda fax, etc.) per le quali il DOS non sia attrezzato, ma è ovviamente possibile utilizzare la tecnica dei device driver anche per attivare gestori più sofisticati di quelli già disponibili nel sistema operativo. Proprio per questo il loro nome completo è installable device driver, in contrapposizione ai resident device driver, routine già presenti nel software di sistema (video, tastiera, dischi, etc.).
Un device driver è, a tutti gli effetti, un programma TSR, ma le accennate modalità di caricamento ed utilizzo impongono (in base alle specifiche Microsoft) che esso abbia una struttura del tutto particolare, purtroppo non coincidente con quella generata dai compilatori C (vedere pag. ): per imparare a scrivere in C un device driver, pertanto, occorre innanzitutto capire come esso è strutturato e come viene caricato ed utilizzato dal DOS
Il bootstrap è la fase iniziale della sessione di lavoro della macchina. Dapprima sono eseguite le routine di autodiagnostica del BIOS, il quale provvede in seguito a cercare sui dischi del computer il loader del DOS: si tratta di una routine, memorizzata in uno dei primi settori del disco fisso (il boot sector), che carica in memoria ed esegue il primo dei due file nascosti del DOS (solitamente chiamato IO.SYS
IO.SYS si compone di una parte residente e di una parte transiente: la prima è destinata a rimanere in memoria fino al successivo spegnimento o reset della macchina, mentre la seconda serve esclusivamente ad effettuare le operazioni di caricamento del sistema. La porzione transiente, infatti, tramite una routine chiamata SYSINIT, individua l'indirizzo del cosiddetto top of memory (il limite superiore della memoria convenzionale) e copia IO.SYS 'lassù', al fine di sgombrare la parte inferiore della RAM. A questo punto in memoria vi sono due istanze di IO.SYS: la SYSINIT della seconda carica l'altro file nascosto (MSDOS.SYS) in modo da ricoprire la porzione transiente della prima. SYSINIT attiva poi tutti i device driver residenti (quelli incorporati nel DOS), legge il file CONFIG.SYS per determinare quali sono gli installable device driver da caricare e provvede al loro caricamento ed inizializzazione: il device driver consente, da questo momento in poi, di accedere alla periferica mediante un nome, che per il sistema operativo equivale (dal punto di vista logico) ad un nome di file o di unità disco, come sarà meglio chiarito tra breve. L'ultima operazione effettuata da SYSINIT è l'esecuzione dell'interprete dei comandi (COMMAND.COM o il programma specificato dall'istruzione SHELL= nel file CONFIG.SYS
Esistono due tipi di device driver: character e block device driver (driver per periferiche a carattere o a blocchi).
I primi sono adatti alla gestione di periferiche come terminali e stampanti, cioè periferiche che effettuano le loro operazioni di I/O un carattere (byte) alla volta. Il nome assegnato dal driver alla periferica può essere usato dal DOS e dalle applicazioni come un nome di file, sul quale scrivere o dal quale leggere i byte. Se il nome è identico a quello già utilizzato da un device driver residente, quest'ultimo è sostituito, nelle sue funzionalità, dall'installable device driver . Il DOS può comunicare con i character device driver in due modalità differenti, a seconda che essi siano definiti come raw o cooked: la modalità raw (grezza) prevede che ogni singolo byte passi dal driver al DOS o viceversa senza alcuna modifica; in modalità cooked (letteralmente 'cucinata', ma con un po' di fantasia si potrebbe tradurre in 'interpretata') il DOS memorizza i caratteri in un buffer e gestisce opportunamente i caratteri di controllo (CTRL‑C, etc.) prima di passarli (al primo RETURN incontrato) all'applicazione o al device driver . La modalità raw o cooked è selezionabile dalle applicazioni, mediante la subfunzione 01h del servizio 44h dell'int 21h:
Int 21h, serv. 44h, subf. 01h: Modifica gli attributi di un device driver
Input |
AH AL BX DH DL |
44h 01h 00h Nuovi bit di stato: Per i bit 0-4 e 6 si rimanda alla descrizione della device attribute word, a pagina |
Output |
AX |
Codice di errore se il CarryFlag Se il CarryFlag la chiamata ha avuto successo |
I block device driver gestiscono periferiche che effettuano l'I/O mediante blocchi di byte (esempio classico: i dischi). Contrariamente alle periferiche a carattere, accessibili esclusivamente in modo sequenziale, i block device sono dispositivi ai quali è possibile accedere in modo random (casuale), cioè con spostamenti arbitrari avanti o indietro rispetto alla posizione attuale nel flusso di dati. Il DOS assegna ad ogni periferica gestita dal device driver un nome di disco (una lettera dell'alfabeto seguita dai due punti): un installable block device driver non può, dunque, sostituirsi a un resident device driver, ma solamente affiancarsi ad esso nella gestione di altre periferiche dello stesso tipo. Va sottolineato, infine, che i block device driver operano sempre in modalità raw.
In cosa consistono le particolarità della struttura dei device driver? Va detto, innanzitutto, che non si tratta di file eseguibili in senso stretto, ma di file dei quali viene caricata in memoria l'immagine binaria: in altre parole, essi vengono caricati in RAM esattamente come sono memorizzati sul disco, senza la creazione di PSP (vedere pag. ) ed environment , né la gestione di una eventuale Relocation Table (pag. ) da parte del sistema operativo.
Inoltre, i primi 18 byte di ogni device driver sono riservati ad una tabella, detta header , contenente informazioni ad uso del DOS (sulla quale ci sofferemeremo tra poco): proprio qui incontriamo uno dei maggiori ostacoli, poiché, come è facile intuire, si tratta di una caratteristica assolutamente incompatibile con la normale modalità di compilazione dei programmi C
Fig. : Struttura di un device driver. |
Ogni device driver, inoltre, incorpora una routine che deve provvedere a salvare, per utilizzi successivi, l'indirizzo del buffer attraverso il quale il DOS comunica con il driver stesso: è la cosiddetta strategy routine (a dire il vero non si vede che cosa ci sia di strategico in tutto ciò, ma comunque).
Il terzo elemento caratteristico dei device driver è, infine, la interrupt routine : anche in questo caso il nome è poco azzeccato, perchè si tratta di una procedura che ha ben poco a che fare con i 'classici' gestori di interrupt (vedere, ad esempio, pag. ); il suo compito consiste nell'individuare il servizio richiesto dal DOS al driver ed attivare la routine (interna al driver) corrispondente.
Ne consegue in modo ovvio che, per ogni servizio gestito, il driver deve incorporare una routine dedicata, più una routine generalizzata di gestione dell'errore per i casi in cui il DOS richieda un servizio non previsto: ogni driver deve però necessariamente gestire è il servizio 00h, corrispondente alla propria inizializzazione in fase di bootstrap
La figura schematizza la struttura di un device driver, coerentemente con le considerazioni sin qui esposte: si vede facilmente che le analogie con i programmi TSR sono molteplici (vedere, ad esempio, la figura a pag.
Ne sappiamo abbastanza, a questo punto, per capire (a grandi linee) come lavorano i device driver: il 'protocollo' di colloquio tra sistema operativo e driver è fisso e si articola in quattro fasi ben definite.
‑ il DOS invoca la strategy routine passandole in ES:BX l'indirizzo (puntatore far) di un buffer (detto request header ) contenente i dati che occorrono per effettuare l'operazione (servizio) prevista (la struttura del buffer varia a seconda del servizio);
‑ la strategy routine salva l'indirizzo del buffer in una variabile (locazione di memoria) nota ed accessibile alle altre routine del driver e restituisce il controllo al sistema;
‑ il DOS invoca la interrupt routine
‑ la interrupt routine accede al buffer (mediante l'indirizzo salvato in precedenza dalla strategy routine) per conoscere il numero del servizio richiesto (e relativi dati) e provvede all'esecuzione delle operazioni per esso previste, generalmente chiamando una routine dedicata, la quale, a sua volta, può chiamare altre routine del driver o interrupt di sistema: al termine, la interrupt routine o le routine dedicate scrivono nel solito buffer i risultati dell'elaborazione ed un valore avente significato di codice di errore, ed infine restituiscono il controllo al DOS.
Fig. : Comunicazione tra applicazione e periferica via device driver. 1) richiesta I/O via int 21h 2) chiamata a strategy routine 3) ritorno al DOS 4) chiamata a interrupt routine 5) pilotaggio periferica 6) risposta della periferica 7) risposta al DOS 8) risposta all'applicazione |
Conseguenza immediata di tale algoritmo è il completo isolamento del device driver dalle applicazioni che ne utilizzano i servizi: i driver, è evidente, interagiscono esclusivamente con il sistema operativo, che si pone quale interfaccia tra essi e le applicazioni. Ciò risulta ancora più palese quando si consideri che i driver rendono accessibili le periferiche loro associate attraverso un nome di file o di unità disco: le applicazioni devono ricorrere agli appositi servizi DOS . Infatti, le applicazioni effettuano le operazioni di I/O richiedendo l'opportuno servizio al sistema operativo (int 21h): il DOS individua (grazie ad una tabella costruita durante il bootstrap) il device driver interessato, costruisce un request header appropriato e chiama la strategy routine. Il device driver memorizza l'indirizzo del buffer e restituisce il controllo al DOS che, immediatamente, chiama la interrupt routine, secondo lo schema analizzato: ne risulta un flusso di processi come quello rappresentato in figura
Vale la pena di osservare che la suddivisione delle operazioni di interfacciamento DOS/driver tra due routine (la strategy e la interrupt) è ispirata alle esigenze di sistemi multitasking (come Unix): essa non è di alcuna utilità in sistemi monotasking (quale è il DOS), in quanto questi eseguono sempre e solo un'unica operazione di I/O alla volta.
Questo è il momento di approfondire l'analisi della struttura dello header, del buffer di comunicazione con il DOS e dei servizi che il device driver può implementare: forse non è divertente, ma è di fondamentale importanza
Il device driver header è la tabella che occupa i primi 18 byte del codice di ogni device driver. Vediamone contenuto e struttura:
Struttura del device driver header
Si noti che i puntatori alla strategy e interrupt routine sono near: il DOS effettua però chiamate far , utilizzando quale parte segmento dell'indirizzo il segmento al quale il driver stesso è caricato. Ne segue che i due campi menzionati contengono, in realtà, la parte offset dell'indirizzo delle due funzioni e che queste devono essere dichiarate entrambe far (e terminare quindi con una istruzione RETF
La tabella che segue descrive il significato dei bit della Device Attribute Word
Struttura della device attribute word
Va sottolineata l'importanza del bit 15, che indica se la periferica gestita lavora a blocchi o a caratteri (vedere pag. ); qualora esso valga (block device driver ), solo i bit 6, 11 e 13‑15 sono significativi: tutti gli altri devono essere impostati a
E' ancora interessante notare che le informazioni contenute nel device driver header sono utilizzate dal sistema operativo, mentre, di norma, le applicazioni non vi accedono. Vi sono però alcuni servizi IOCTL che consentono di leggere e modificare alcuni dei bit (non tutti) della attribute word
Il request header è il buffer attraverso il quale DOS e device driver si scambiano le informazioni: il sistema operativo ne carica l'indirizzo in ES:BX e chiama la strategy routine perché il device driver possa conoscerlo e memorizzarlo. Il buffer contiene il numero del servizio richiesto, nonché tutti i dati necessari al driver per il suo espletamento, e si divide in due parti: la prima, detta parte fissa, è identica (quanto a numero, ordine e dimensione dei campi) per tutti i servizi, mentre la seconda, detta parte variabile, ha struttura differente a seconda del servizio richiesto (alcuni servizi presentano comunque identica parte variabile del request header, o non la utilizzano del tutto). La parte fissa del request header è strutturata come segue.
Struttura del device driver request header
OFFSET |
DIM. |
CONTENUTO |
00h |
|
Lunghezza totale del request header. Il valore, diminuito di 13 (lunghezza della parte fissa) esprime la lunghezza della parte variabile. E' un campo utilizzato assai raramente, dal momento che la lunghezza del request header può essere desunta dal numero del servizio richiesto. |
01h |
|
Numero dell'unità (disco). E' un campo significativo solo per i block device driver e indica su quale disco (o altro block device) deve essere eseguito il servizio richiesto. |
02h |
|
Command code. E' il numero del servizio richiesto dal DOS al device driver. |
03h |
|
Return Code (Status word). E' il valore restituito dal driver al DOS per indicare lo stato del servizio eseguito. |
05h |
|
Utilizzo riservato al DOS. |
E' importante approfondire l'analisi del Return Code : si tratta di una word (due byte) nella quale il byte più significativo è interpretato come campo di bit, ed è usato per indicare lo stato del servizio; il byte meno significativo contiene invece un valore che descrive un errore se il bit 15 della word vale 1: se questo è 0, detto valore viene ignorato dal sistema operativo. Il dettaglio dei bit e codici di errore è riportato nella tabella che segue.
Struttura della device driver status word
Bit |
Significato |
|
|
Error Flag . 1 se si è verificato un errore, 0 altrimenti. |
|
|
Riservati. |
|
|
Busy Flag . 1 se il driver vuole impedire al DOS di richiedere ulteriori servizi (ad esempio perché l'esecuzione del servizio non è stata ancora portata a termine), 0 altrimenti. |
|
|
Done Flag . 1 se il servizio è stato completamente eseguito, 0 se l'operazione non è ancora stata completata. |
|
|
Error Code (codice di errore). E' significativo solo se il bit 15 (Error Flag) è 1: |
|
|
00h |
Tentativo di scrittura su unità protetta. |
|
01h |
Unità sconosciuta. |
|
02h |
Unità non pronta (ad es.: sportello del disk drive aperto). |
|
03h |
Servizio non supportato. |
|
04h |
Errore di CRC. |
|
05h | |
|
06h |
Errore di ricerca dati sull'unità (seek error). |
|
07h |
Tipo di unità sconosciuto. |
|
08h |
Settore non trovato. |
|
09h |
Stampante senza carta. |
|
0Ah |
Errore di scrittura. |
|
0Bh |
Errore di lettura. |
|
0Ch |
Errore generico, non individuato (General failure). |
|
0Dh |
Riservato. |
|
0Eh |
Riservato. |
|
0Fh |
Come si è detto, la parte variabile del request header è strutturata in dipendenza dal servizio richiesto dal DOS al driver, cioè a seconda del valore che il campo Command code assume. Si noti che il driver restituisce valori e informazioni al DOS scrivendoli, a sua volta, nel request header (in campi della parte fissa o variabile). Di seguito è presentato l'elenco completo dei servizi che il DOS può richiedere al driver.
Elenco dei servizi implementabili dai device driver
Codice |
Servizio |
|
Init (inizializzazione del driver). |
|
Media Check (solo per block device driver) |
|
Build BIOS Parameter Block (solo per block device driver) |
|
IOCTL Read |
|
Read (input) |
|
Nondestructive Read (solo per character device driver) |
|
Input Status (solo per character device driver) |
|
Flush Input Buffers (solo per character device driver) |
|
Write (output) |
|
Write With Verify |
|
Output Status (solo per character device driver) |
|
Flush Output Buffers (solo per character device driver) |
|
IOCTL Write |
|
Device Open |
|
Device Close |
|
Removable Media (solo per block device driver) |
|
Output Until Busy (solo per character device driver) |
|
Generic IOCTL Request |
|
Get Logical Device (solo per block device driver) |
|
Set Logical Device (solo per block device driver) |
I servizi 13‑16 sono stati introdotti a partire dalla versione 3.0 del DOS, mentre i servizi 19 e 23‑24 dalla versione 3.2. Di seguito sono analizzati nel dettaglio tutti i servizi elencati e, per ciascuno di essi, la corrispondente struttura della parte variabile del device driver request header.
Il servizio 0 è la routine di inizializzazione del driver, detta Init. Esso è richiesto dal DOS una volta sola, nella fase di caricamento del driver durante il bootstrap . Il driver ha così la possibilità di effettuare tutte le operazioni necessarie alla predisposizione dell'operatività successiva: controllo dello hardware, installazione di gestori di interrupt e via dicendo. L'utilizzo del request header è il seguente:
Device driver, serv. 00: uso del request header
Bisogna tenere presente che quando i device driver vengono caricati dal DOS, quest'ultimo non ha ancora completato la propria installazione e, pertanto, non tutte le funzionalità che esso implementa sono disponibili. In particolare, Microsoft afferma che il servizio 0 dei device driver può utilizzare solo alcune delle funzioni dell'int 21h: da 01h a 0Ch (I/O di caratteri), 25h e 35h (installazione e richiesta di vettori di interrupt), 30h (richiesta della versione DOS). In realtà, esperimenti empirici hanno rivelato che altre funzioni sono attive e disponibili: particolarmente importanti risultano quelle relative all'I/O con i file (durante la fase di init è quindi possibile, ad esempio, leggere un file di configurazione specificato sulla riga di comando del device driver in CONFIG.SYS
Il sistema operativo, nel richiedere il servizio 0, consente al driver di conoscere la riga di comando nel file CONFIG.SYS, come specificato circa il campo ad offset 12h nel request header. La stringa, tutta in caratteri maiuscoli, termina al primo LF o CR o EOF 10h o 13h o 1Ah) e non deve essere modificata dal driver, che può però copiarla in una locazione di memoria privata per effettuare tutte le elaborazioni eventualmente necessarie.
Al termine della Init, il driver deve porre a il bit 8 (Done Flag) della status word (campo ad offset 03h del request header), e deve indicare al DOS quanta memoria deve essere riservata per la parte residente: particolarmente importante allo scopo risulta il campo ad offset 0Eh, in quanto consente al driver di specificare l'indirizzo del primo byte di RAM oltre la porzione residente. Il DOS sa, in tal modo, che a quell'indirizzo inizia la memoria disponibile per le successive operazioni di bootstrap. Detto indirizzo può validamente essere, ad esempio, quello della prima funzione della parte transiente nel sorgente C (se la parte residente non usa funzioni di libreria). Se il driver rileva, durante l'inizializzazione, errori tali da renderne inutile il caricamento (ad esempio: il driver del mouse non ne trova alcuno collegato al personal computer), può scrivere nel campo in questione l'indirizzo del proprio device driver header: essendo questo l'indirizzo al quale il driver stesso è caricato in memoria, il sistema operativo non riserva neppure un byte alla parte residente, risparmiando così preziosa memoria. Per segnalare al DOS la condizione di 'aborted installation' occorre anche azzerare il bit 15 della device attribute word nel device driver header (pag. ) e restituire nel campo ad offset 0Dh del request header.
Il driver (se è un block device driver ) può anche conoscere il numero assegnato alla prima delle sue unità grazie all'ultimo campo della parte variabile del request header; esso deve comunicare al DOS il numero di unità supportate (campo ad offset 0Dh), tramite il quale il DOS assegna loro gli identificativi letterali , nonché gli indirizzi (campo ad offset 12h) dei BPB che descrivono ogni unità. Il BPB (BIOS Parameter Block) è una tabella che contiene i parametri BIOS per un'unità disco e ha il formato descritto di seguito:
Struttura del BPB
OFF |
DIM |
CAMPO |
00h |
|
Lunghezza del settore in byte |
02h |
|
Numero di settori per cluster (unità di allocazione) |
03h |
|
Numero di settori riservati (a partire dal settore 0) |
05h |
|
Numero di FAT (File Allocation Table) presenti sul disco |
06h |
|
Numero massimo di elementi nella directory root |
08h |
|
Numero totale di settori |
0Ah |
|
Media descriptor byte (o Media ID byte): F0h: floppy 3.5' (2 lati, 18 sett./traccia) |
0Bh |
|
Numero di settori in una FAT |
0Dh |
|
Numero di settori per traccia (dal DOS 3.0 in poi) |
0Fh |
|
Numero di testine (dal DOS 3.0 in poi) |
11h |
|
Numero di settori nascosti (dal DOS 3.0 in poi) |
13h |
|
Word più significativa del numero di settori nascosti, a partire dal DOS 3.2 (in cui il numero settori nascosti diviene un dato a 32 bit) |
|
|
Se la word ad offset 08h , questo campo contiene il numero totale di settori espresso come dato a 32 bit, onde consentire il superamento del limite di 32Mb alla dimensione delle partizioni dei dischi fissi (dal dos 3.2 in poi). |
Il servizio 1 è specifico dei block device driver ed è richiesto dal DOS durante operazioni di I/O diverse dalle semplici lettura o scrittura. In pratica, il sistema chiede al driver di verificare se il disco sul quale le operazioni sono in corso è stato sostituito: il driver può rispondere 'SI', 'NO' oppure 'NON SO'. Nel primo caso, il DOS non riutilizza il contenuto dei buffer relativi a quella unità, richiede un servizio 2 (descritto di seguito) e rilegge la FAT e la root directory. Se il driver risponde 'NO', allora il sistema effettua l'oprazione di I/O richiesta dall'applicazione considerando valido il contenuto dei buffer. Nel caso di risposta dubitativa, il DOS opera in modo differente a seconda dello stato dei buffer: se questi contengono dati in attesa di essere scritti, il sistema li scrive (anche a rischio di danneggiare il contenuto del disco nel caso in cui esso sia stato effettivamente sostituito); in caso contrario procede come per la risposta 'SI'.
Il request header è strutturato come segue:
Device driver, serv. 01: uso del request header
Il device driver può utilizzare il campo ad offset 1Fh se il bit 11 della device attribute word (pag. ) vale
Anche questo servizio è tipico dei block device driver. Il DOS lo richiede dopo un servizio 1 (testè descritto) in due casi: se il device driver di unità restituisce un Media Change Code (disco sostituito), oppure restituisce (disco forse sostituito) e non vi sono buffer contenenti dati in attesa di essere scritti. La chiamata al servizio 2 consente al DOS di conoscere le caratteristiche del nuovo disco presente nell'unità e 'legalizza' la sostituzione avvenuta. Il request header è utilizzato come segue:
Device driver, serv. 02: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero di unità disco ( A: |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
Media ID byte (pag. |
|
0Eh |
|
Puntatore far ad un buffer contenente il primo settore della FAT del disco. |
|
12h |
|
|
Puntatore far al BPB (pag. ) del disco presente nell'unità. |
Il buffer il cui indirizzo è ricevuto dal driver nel campo ad offset 0Eh non deve essere modificato dal driver stesso se l'unità gestita è formattata secondo lo standard IBM; in caso contrario il buffer può essere utilizzato come area di lavoro.
Il servizio 3 deve essere supportato dal driver solo se il bit 14 della device attribute word (pag. . Esso è solitamente utilizzato dalle applicazioni per ottenere dal device driver informazioni sullo stato o sulla configurazione della periferica, senza trasferimento di dati, attraverso le apposite subfunzioni dell'int 21h, funzione 44h. La struttura del request header è descritta nella tabella che segue.
Va infine osservato che le applicazioni possono leggere e scrivere dati da un character device solo dopo averlo 'aperto', utilizzando il nome logico come se si trattasse di un vero e proprio file: per un esempio si veda pag.
Vedere anche il servizio 12, pag.
Device driver, serv. 03: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero di unità disco ( A:); usato solo dai block device driver |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
Media ID byte (pag. ); usato solo dai block device driver |
|
0Eh |
|
Puntatore far ad un buffer utilizzato per il trasferimento delle stringhe di controllo. |
|
12h |
|
Numero di byte disponibili nel buffer |
Numero di byte utilizzati nel buffer |
Il servizio 4 consente il trasferimento di dati dalla periferica ad un buffer in memoria. Il request header è strutturato come dalla tabella che segue.
Dal momento che (per i block device driver ) il DOS gestisce i settori dei dischi come settori logici numerati progressivamente a partire da 0, il device driver, per effettuare l'operazione di lettura deve trasformare (se necessario) il dato ricevuto nel campo ad offset 14h in un numero di settore fisico, esprimibile mediante le coordinate 'tridimensionali' BIOS : lato/traccia/settore . A partire dal DOS 3.0, i block device driver possono servirsi dei dati gestiti mediante i servizi 13 (pag. ) e 14 (pag. ) per determinare se il disco sia stato sostituito 'imprudentemente'. Vedere anche i servizi 8 a pag. e 9 a pag.
Device driver, serv. 04: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero di unità disco ( A:); usato solo dai block device driver |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
Media ID byte (pag. ); usato solo dai block device driver |
|
0Eh |
|
Puntatore far ad un buffer utilizzato per il trasferimento dei dati |
|
12h |
|
Numero di byte (character device driver) o settori (block device driver) di cui è richiesto il trasferimento |
Numero di byte (character device driver) o settori (block device driver) di cui è effettuato il trasferimento |
14h |
|
Numero del settore logico di partenza; usato solo dai block device driver |
|
16h |
|
|
A partire dal DOS 3.0: puntatore far ad una stringa ASCIIZ (terminata con un NULL) contenente l'etichetta di volume del disco se si è verificato un errore 0Fh (vedere pag. |
Va infine osservato che le applicazioni possono leggere e scrivere dati da un character device solo dopo averlo 'aperto', utilizzando il nome logico come se si trattasse di un vero e proprio file: per un esempio si veda pag.
Il servizio 5 è supportato solo dai character device driver ed è utilizzato dal DOS per ispezionare il prossimo byte presente nel flusso di dati proveniente dalla periferica (nel caso della tastiera, lo scopo specifico è individuare eventuali sequenze CTRL‑C ). La struttura del request header è descritta nella tabella che segue.
Se nel flusso dati vi è effettivamente un carattere in attesa di essere letto, il driver deve restituirlo nel campo ad offset 0Dh; esso deve inoltre porre a il Busy Flag Bit nella word di stato dell'operazione. Se non vi è alcun carattere in attesa, il driver deve unicamente porre a detto bit.
Device driver, serv. 05: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
|
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
|
Prossimo byte nel flusso di dati proveniente dalla periferica |
Le applicazioni possono leggere e scrivere dati da un character device solo dopo averlo 'aperto', utilizzando il nome logico come se si trattasse di un vero e proprio file: per un esempio si veda pag.
Il servizio 6 è supportato solamente dai character device driver. Esso è utilizzato dal DOS per verificare se vi sono caratteri in attesa di essere letti nel flusso di dati proveniente dalla periferica ed utilizza la sola parte fissa del request header.
Device driver, serv. 06: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
|
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Il driver deve porre a il Busy Flag Bit nella Status Word se non vi è alcun carattere in attesa; in caso contrario deve porre detto bit a . Se il device non dispone di un buffer (hardware, o implementato via software dal driver stesso) detto bit deve essere sempre . Questo servizio è il supporto di basso livello alla funzione 06h dell'int 21h (Check Input Status). Vedere anche il servizio 10.
Il servizio 7 è supportato solo dai character device driver. E' utilizzato dal DOS per richiedere al driver di eliminare tutti i caratteri in attesa di lettura presenti nei buffer associati alla periferica, rendendo questi ultimi disponibili per nuove operazioni di lettura. E' utilizzata solo la parte fissa del request header
Device driver, serv. 07: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
|
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Vedere anche il servizio 11 a pag.
Il servizio 8 consente il trasferimento di dati da un buffer in memoria alla periferica. Circa la struttura del request header e le particolarità operative, si veda il servizio 4 (Read), a pag. , con l'avvertenza che il Command Code è, ovviamente, invece di . Vedere anche il servizio 9.
Va ancora osservato che le applicazioni possono leggere e scrivere dati da un character device solo dopo averlo 'aperto', utilizzando il nome logico come se si trattasse di un vero e proprio file: per un esempio si veda pag.
Il servizio 9 è analogo al servizio 8, testè descritto, ma dopo l'operazione di scrittura il driver deve effettuare una operazione di lettura dei dati appena scritti per verificarne la correttezza. Il command code è . Vedere anche il servizio 4 a pag.
Il servizio 10 è supportato solamente dai character device driver. Esso è utilizzato dal DOS per verificare se un'operazione di scrittura da parte del driver sia in corso. Come per il servizio 6, è utilizzata solo la parte fissa del request header (ma il Command Code è ); il driver deve porre a il Busy Flag Bit nella Status Word (pag. ) se una operazione di scrittura è in corso. In caso contrario (condizione di idle driver) il bit deve essere azzerato. Questo servizio è il supporto di basso livello alla funzione 07h dell'int 21h (Check Output Status).
Il servizio 11 è supportato solo dai character device driver. E' utilizzato dal DOS per richiedere al driver di completare tutte le operazioni di scrittura in corso, trasferendo fisicamente alla periferica tutti i dati presenti nei buffer ad essa associati e rendendo questi ultimi nuovamente disponibili per nuove operazioni di scrittura. Come per il servizio 7 (pag. ), è utilizzata solo la parte fissa del request header, ma il Command Code è, ovviamente,
Il servizio 12 deve essere supportato dal driver solo se il bit 14 della device attribute word (pag. . Esso è solitamente utilizzato dalle applicazioni per inviare al device driver direttive di configurazione della periferica, senza trasferimento di dati, attraverso le apposite subfunzioni dell'int 21h, funzione 44h. La struttura del request header è identica a quella presentata con riferimento al servizio 3 (pag. ), con le sole differenze che il Command Code vale e che il campo ad offset 0Eh del request header passato dal DOS al driver indica il numero di byte utilizzati nel buffer; il medesimo campo restituito dal driver esprime il numero di byte che esso ha effettivamente inviato alla periferica.
Va infine osservato che le applicazioni possono leggere e scrivere su un character device solo dopo averlo 'aperto', utilizzando il nome logico come se si trattasse di un vero e proprio file: per un esempio si veda pag.
Il servizio 13 deve essere supportato dai driver che hanno il bit 11 della device attribute word (pag. ) posto a ed è richiesto dal DOS a partire dalla varsione 3.0. E' utilizzata solo la parte fissa del request header
Device driver, serv. 13: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero dell'unità (solo per i block device driver) |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Il driver non restituisce al DOS alcun risultato: scopo principale del servizio è consentire al driver di gestire un contatore dei file aperti sulla periferica supportata: questo deve essere incrementato dal servizio in esame e decrementato dal servizio 14 (Device Close; commentato di seguito). Il contatore deve inoltre essere forzato a dal driver quando sia intercettata la sostituzione del disco nell'unità (vedere i servizi 1 e 2 a pag. e seguenti).
Il servizio 13 è richiesto dal DOS in corrispondenza di tutte le chiamate da parte di applicazioni alle funzioni dell'int 21h che gestiscono l'apertura o la creazione di file e l'apertura di un character device per input o output. Può essere utilizzato, dai character device driver, per inviare alle periferiche stringhe di inizializzazione, eventualmente ricevute dalle applicazioni (via DOS) attraverso il servizio 12, testè commentato.
L'apertura di un character device driver viene effettuata come una apertura di file utilizzando il nome logico del device.
Il servizio 14 è la controparte del servizio 13, testè descritto; pertanto anch'esso deve essere supportato dai driver che hanno il bit 11 della device attribute word (pag. ) posto a . Come per il servizio 13, è utilizzata solo la parte fissa del request header (ma il Command Code è ) e il driver non restituisce al DOS alcun risultato: scopo principale del servizio è la gestione, in 'collaborazione' con il servizio 13, di un contatore dei file aperti sulla periferica.
Il servizio 14 è richiesto dal DOS in corrispondenza di tutte le chiamate da parte di applicazioni alle funzioni dell'int 21h che gestiscono la chiusura di file e la chiusura di un character device al termine di operazioni di input o output. Può essere utilizzato, dai character device driver, per inviare alle periferiche stringhe di reset o reinizializzazione, eventualmente ricevute dalle applicazioni (via DOS) attraverso il servizio 12 (pag.
Il servizio 15 è supportato dai block device driver che hanno il bit 11 della device attribute word (pag. ) posto a ed è richiesto dal DOS a partire dalla versione 3.0. Esso costituisce il supporto, a basso livello, della subfunzione 08h della funzione 44h dell'int 21h (CheckIfBlockDeviceIsRemovable). E' utilizzata solo la parte fissa del request header:
Device driver, serv. 15: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero dell'unità |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Il driver deve porre a il Busy Flag Bit (bit 11) della Status Word se il disco non è rimuovibile; in caso contrario detto bit deve essere azzerato.
Il servizio 16 è supportato esclusivamente dai character device driver che hanno il bit 13 della device attribute word posto a ed è richiesto dal DOS a partire dalla versione 3.0. Il suo scopo principale è permettere l'implementazione di una routine ottimizzata di output per il pilotaggio in background di periferiche (ad esempio stampanti con spooler). Il request header è strutturato come da tabella:
Device driver, serv. 16: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
|
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
|
|
0Eh |
|
Puntatore far ad un buffer utilizzato per il trasferimento dei dati |
|
12h |
|
Numero di byte di cui è richiesta la scrittura verso la periferica |
Numero di byte effettivamente trasferiti |
Il driver trasferisce i dati alla periferica, in modo continuo, sino al loro esaurimento e restituisce al sistema operativo il numero di byte effettivamente trasferiti
Il servizio 19 è definito a partire dalla versione 3.2 del DOS, che lo richiede solo se il bit 6 della device attribute word è . Esso costituisce il supporto di basso livello per la subfunzione 0Ch della funzione 44h dell'int 21h e ha quale principale finalità fornire supporto alla realizzazione di una interfaccia IOCTL con le caratteristiche desiderate dal programmatore . Il request header è utilizzato come segue:
Device driver, serv. 19: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero di unità disco ( A:); usato solo dai block device driver |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
0Dh |
|
Category Code (Major Code) |
|
0Eh |
|
Function Code (Minor Code) |
|
0Fh |
|
Contenuto del registro SI |
|
11h |
|
Contenuto del registro DI |
|
13h |
|
Puntatore far ad un buffer (Generic IOCTL Data Packet) contenente i dati necessari al servizio |
|
L'interfaccia IOCTL supportata dal servizio 19 è di tipo aperto, disponibile, cioè, per future implementazioni. Tutte le sue caratteristiche e funzionalità, in ogni caso, devono essere definite dal programmatore. Il driver riceve dal DOS Category Code e Function Code, che possono essere utilizzati, rispettivamente, per identificare un tipo di driver e selezionare il sottoservizio desiderato. L'utilizzo dei valori di SI e DI copiati nel request header è libero. Infine, anche la dimensione, il formato ed il contenuto del buffer, il cui indirizzo è memorizzato nell'ultimo campo del request header, sono definiti dal programmatore a seconda della modalità scelta per l'implementazione del servizio. Già a partire dalla versione 3.3 del DOS si sono diffusi alcuni utilizzi standard del servizio, con valori predefiniti per Category Code, Function Code e formato del buffer; ciononstante, nessuno di essi è ufficialmente documentato.
Analoghe considerazioni valgono per eventuali valori restituiti dal driver al DOS: può essere utilizzato qualsiasi campo del request header (eccetto il campo di status) o lo stesso buffer.
Per un esempio di utilizzo del servizio 19, si veda pag.
Il servizio 23 è supportato dai block device driver nella cui device attribute word il bit 6 è posto a ed è richiesto dal DOS a partire dalla versione 3.2. Esso costituisce il supporto di basso livello per la sottofunzione 0Eh della funzione 44h dell'int 21h (GetLogicalDriveMap), il cui scopo è determinare se alla medesima unità disco sia assegnata più di una lettera. La struttura del request header è la seguente:
Device driver, serv. 23: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero dell'unità |
Codice indicativo dell'ultima lettera utilizzata per referenziare l'unità ( A: B:, etc.). |
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Se il driver supporta una sola unità disco deve restituire nel campo ad offset 01h nel request header. Vedere anche il servizio 24, commentato di seguito.
Il servizio 24 è supportato dai block device driver nella cui device attribute word il bit 6 è posto a ed è richiesto dal DOS a partire dalla versione 3.2. Esso costituisce il supporto di basso livello per la sottofunzione 0Fh della funzione 44h dell'int 21h (SetLogicalDriveMap) ed è la controparte del servizio 23, testè descritto. Il suo scopo è far conoscere al driver la successiva lettera con cui è referenziata l'unità disco. La struttura del request header è la seguente:
Device driver, serv. 24: uso del request header
OFF |
DIM |
Request Header Ricevuto |
Request header Restituito |
00h |
|
Lunghezza del request header |
|
01h |
|
Numero dell'unità |
|
02h |
|
Numero del servizio richiesto ( |
|
03h |
|
|
Stato dell'operazione (pag. |
05h |
|
|
|
Il numero di unità passato dal DOS al driver è relativo, con base , alla prima unità supportata dal driver stesso
Il concetto di device driver installabili è stato introdotto con la versione 2.0 del DOS. La versione 1.0 incorporava tutti i device driver previsti e non era possibile caricarne di nuovi.
I device driver non sono mai caricati lanciandoli al prompt del dos, come avviene per i normali programmi: al contrario essi vengono installati in memoria dal sistema operativo stesso durante il bootstrap, se specificato nel file CONFIG.SYS mediante l'istruzione DEVICE=, ad esempio:
DEVICE=C:UTMOUSE.SYS
Alcuni device driver residenti hanno nomi piuttosto noti (vedere pag. CON (tastiera/video), AUX (prima porta seriale), PRN (prima porta parallela), NUL (device nullo: una specie di buco nero). Nulla vieta di scrivere nuovi installable device driver per la loro gestione: è sufficiente, ad esempio, che un device driver si registri al DOS come AUX perchè il sistema lo utilizzi come nuovo programma di pilotaggio della porta seriale.
La subfunzione 00h del servizio 44h dell'int 21h consente all'applicazione di conoscere gli attributi del driver. Essa è del tutto analoga alla 01h, ma non prevede alcun input in DX, mentre AL deve essere, evidentemente, 00h. In uscita, il registro DX contiene gli attributi del driver, come descritto a pag. , con la sola eccezione del bit 6, che vale se si è verificata una condizione di EOF nell'ultima operazione di input dal device.
Vedremo tra breve che un character device, grazie alla 'intermediazione' svolta dal driver, è accessibile via handle, in modo analogo a quanto avviene per i file (vedere pag.
Un block device driver può gestire più periferiche contemporaneamente, al contrario dei character device driver, che possono pilotarne una soltanto.
Non sono, cioè, files .EXE o .COM. Per la precisione, il DOS è in grado, a partire dalla versione 3.0, di caricare device driver che si presentino come files .EXE: questi sono tali a tutti gli effetti, ma la loro struttura interna non differisce da quella della generalità dei device driver e permangono, di conseguenza, tutte le difficoltà del caso qualora si intenda scrivere in C un device driver indipendentemente dal fatto che si voglia o no ottenere, quale risultato, un file .EXE. Rimane da sottolineare, al riguardo, che la maggior parte dei device driver è costituita da file con estensione .SYS: non è un requisito di sistema, ma semplicemente una convenzione largamente condivisa e seguita.
La mancanza di un environment (variabili di ambiente) appare del resto ovvia, quando si pensi che, al momento del caricamento dei device driver, l'interprete dei comandi (che è incaricato della generazione dell'environment per tutti i processi) non è ancora stato lanciato.
Vale la pena di ricordare che in testa ad ogni eseguibile generato dalla compilazione di un sorgente C vi è, normalmente, il codice del modulo di startup. Inoltre, se il programma è un .EXE, in testa al file deve esserci la Relocation Table.
La interrupt routine si differenzia dagli interrupt, tra l'altro, in quanto è attivata dal DOS (pertanto non la chiama l'applicazione in foreground né un evento hardware asincrono) e non termina con una istruzione IRET (bensì con una normalissima RETF). Tutto ciò non significa, comunque, che un device driver non possa incorporare gestori di interrupt qualora il programmatore lo reputi necessario.
Come fa il DOS a conoscere gli indirizzi della strategy e della interrupt routine, in modo da poterle chiamare? Semplice: essi sono contenuti nella tabella di 18 byte in testa al device driver.
Una parziale eccezione si ha quando il device driver incorpora routine di gestione di interrupt che le applicazioni utilizzano direttamente. Si tratta di un comportamento lecito ma, in qualche misura, anomalo e maggiormente analogo a quello dei programmi TSR che a quello caratteristico dei device driver.
Va detto che un unico file può contenere più di un device driver. In questo caso, in testa al codice di ogni device driver deve trovarsi un device driver header: il campo ad offset 00h dovrà essere correttamente inizializzato a cura del programmatore con l'indirizzo del successivo driver (cioè con l'indirizzo del primo byte del successivo header) in tutti gli header eccetto l'ultimo, che contiene ancora il valore FFFFFFFFh ‑1L
IOCTL significa Input/Output Control. Si tratta di una modalità di gestione delle periferiche mediante stringhe di controllo, che a livello DOS è supportata dal servizio 44h dell'int 21h.
Sono le funzioni 00h e 01h del servizio 44h dell'int 21h (GetDeviceInfo e SetDeviceInfo). La funzione 00h consente, tra l'altro, di conoscere se ad un nome logico di file è associato un device o un vero e proprio file (per la descrizione del servizio vedere pag. ; per un esempio si veda pag.
Usato solo dai programmatori che hanno tempo da perdere per sviluppare una routine di controllo. Quasi tutti si fidano, a torto o a ragione, del DOS.
Ne segue che, se il codice della Init si trova in coda al sorgente e il driver non utilizza funzioni di libreria una volta terminata la fase di caricamento (cioè nelle routine che implementano gli altri servizi), l'occupazione di memoria può essere ridotta escludendo la Init stessa dalla porzione residente (vedere, per analogia, pag.
Mancano all'appello, ad esempio, l'interprete dei comandi, il master environment, la catena dei memory control block (vedere pag. ), etc..
Facciamo un esempio. Se il DOS 'dice' al driver che la prima unità libera è la numero 3, ciò significa che al momento del caricamento del driver sono presenti nel sistema 3 unità disco, numerate da 0 a 2 e chiamate A: B: e C:. Se, al termine della Init, il driver 'risponde' di supportare 2 unità, queste verranno numerate 3 e 4 e saranno loro attribuiti gli identificativi D: ed E:
Se il disco non ha etichetta di volume è consentito restituire un puntatore far alla stringa 'NO NAME
Il che non è proprio immediato. Va tenuto presente, infatti, che il primo settore gestito dal DOS è il Boot Sector, che ha numero logico 0: sui floppy disk esso è anche il primo settore fisico del disco, cioè il primo settore della prima traccia (o cilindro) del primo lato (o testina), e ha coordinata BIOS (lato/traccia/settore) 0,0,1. Sui dischi fissi, invece, esso è il primo settore della prima traccia del secondo lato (coordinata BIOS 0,1,1). Conoscendo il tipo di disco, il numero dei settori per traccia, il numero di tracce per lato ed il numero di lati (un hard disk ha, di solito, più di due lati, essendo costituito da più dischi montati su di un perno) con cui il disco è formattato è possibile convertire il numero logico DOS in coordinata BIOS e viceversa.
Appunti su: |
|