Questa sezione è dedicata a quelle persone che vogliono utilizzare al massimo
il proprio lettore di CD. In maniera perticolare mi occuperò della lettura
delle tracce audio in formato digitale e di come si possa sapere se il proprio
lettore di CD supporta questa funzione.
Purtroppo non ho abbastanza conoscenza dei formati Audio digitali e quindi sono
costretto a limitare la mia trattazione a livello di utilizzo di tutte le
funzionalità del lettore di CD. In modo particolare vorrei suggerire un
insieme di strutture in C che ho realizzato a partire dalla documentazione di IBM
che mi sembrano abbastanza versatili per qualsiasi tipo di utilizzo. Personalmente
mi sono spinto oltre ed ho inglobato queste strutture in una serie di classi C++
che semplificano notevolmente l'utilizzo del CD come device. Se qualcuno fosse
interessato a queste classi può contattarmi via e-mail (ancora però
non sono complete).
Le DevIOCtls
Per sfruttare al massimo le capacità dei lettori di CD-ROM è
necessario accedere direttamente al device driver. In OS/2 per comunicare con i
device driver si utilizzano le famose DevIOCtls (cioè Device I/O controls)
queste altro non fanno che inviare al driver dei comandi specifici che vengono
identificati da due numeri:
il primo identifica la categoria, cioè il tipo di dispositivo che si vuole
controllare e il secondo identifica la funzione che si vuol ottenere.
CD DevIOCtls
Categoria |
Funzione |
Commento |
80h CD-ROM Drive & Disc |
40h |
Reset Drive |
44h |
Eject Disc |
45h |
Close Tray |
46h |
Lock/Unlock Door |
50h |
Seek |
60h |
Device Status |
61h |
Identify CD-ROM Driver |
63h |
Return Sector Size |
70h |
Report Location of Drive Head |
72h |
Read Long |
78h |
Return Volume Size |
79h |
Get UPC |
81h CD-ROM Audio |
40h |
Set Audio-Channel Control |
50h |
Play Audio |
51h |
Stop Audio |
52h |
Resume Audio |
60h |
Return Audio-Channel Information |
61h |
Return Audio-Disc Information |
62h |
Return Audio-Track Information |
63h |
Return Audio-Subchannel Q Information |
65h |
Return Audio-Status Information |
Prima di poter usare la DevIOCtls bisogna ottenere tramite la DosOpen un handle per
il device. Nel caso di un lettore di CD a cui è assegnata la lettera di unità H si può lo si può ottenere scrivendo il seguente codice:
HFILE hDev;
ULONG ulAction;
APIRET rc;
ulAction=0; // azione intrapresa da DosOpen
rc=DosOpen( "H:", // Nome del device
&hDev, // puntatore all'handle
&ulAction, // puntatore all'azione
0, // non usato per i device
0, // non usato
OPEN_ACTION_OPEN_IF_EXISTS,
OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE
| OPEN_FLAGS_DASD,
NULL);
Se rc è uguale a NO_ERROR la variabile hDev contiene un handle valido per il device.
A questo punto possiamo usare la DosDevIOCtl, vediamo quindi quali sono i parametri più importanti:
APIRET rc;
rc=DosDevIOCtl( hDevice, //handle per il device
category, //categoria
function, //funzione
pParam,
cbParmLenMax,
pcbParmLen,
pData,
cbDataLenMax,
pcbDataLen);
I due puntatori pParam e pData fanno riferimento a due strutture, rispettivamente, per i parametri in ingresso e per i parametri in uscita. La definizione delle strutture varia da funzione a funzione e quindi entrambi i puntatori non sono tipizzati. Gli altri quattro parametri indicano lo spazio massimo allocato per ciascuna struttura (cbParmLenMax e cbDataLenMax ) e lo spazio effettivamente usato nelle strutture (pcbParmLen e pcbDataLen ). Attenzione che gli ultimi due parametri sono puntatori in quanto al ritorno della funzione le due variabili puntate contengono il numero di byte della struttura effettivamente utilizzati, e, nel caso che non ci fosse spazio sufficiente, contengono il numero di byte che sarebbero necessari per completare il comando con successo.
È necessario prestare particolare attenzione ai parametri passati a questa funzione perché le strutture vengono passate direttamente al device driver ( a ring 0 !) e quindi se si sono passati dei valori non corretti si può anche piantare il sistema. Inoltre è opportuno tener presente che la maggior parte dei compilatori allinea gli elementi delle strutture in modo da ottimizzarne l'accesso, ma essendo i device driver scritti in assembler le strutture sono impacchettate senza lasciare alcuno spazio vuoto tra un elemto e l'altro. Questo problema può essere facilmente aggirato ricorrendo alla direttiva #pragma pack(1) , disponibile praticamente in tutti i compilatori C/C++.
Le strutture
L'insieme di strutture da me realizzato mira a ridurre al minimo il numero di passaggi necessario per passare da una chiamata DosDevIOCtl e l'altra, a scapito magari di un maggior uso di memoria (in realtà assai limitato).
L'idea su cui si basa questo lavoro deriva dall'osservazione che alcuni gruppi di funzioni hanno strutture di parameri e dati molto simili (in genere hanno una parte comune e degli elementi specifici aggiunti). Quindi per ottenere il mio scopo ho cercato di raggruppare le funzioni e creare una struttura (composta a sua volta da altre strutture e unions) che permettesse di usare tutte le funzioni.
Ed ecco le due strutture risultanti:
#pragma pack(1)
typedef struct _CD_Param
{
ULONG Signature;
union
{
struct _CD_Play_Param Play;
struct _CD_TrackInfo_Param TrackInfo;
struct _CD_LockUnLock_Param Lock;
struct _CD_Seek_Param Seek;
struct _CD_GetHeadLoc_Param Head;
struct _CD_ReadLong_Param Read;
} p;
} CD_Param;
typedef union _CD_Data
{
struct _CD_TrackInfo_Data TrackInfo;
struct _CD_DiskInfo_Data DiskInfo;
struct _CD_ChannelCtrl_Data ChannelCtrl;
struct _CD_SubchannelQ_Data SubchannelQ;
struct _CD_AudioStatus_Data AudioStatus;
struct _CD_DeviceStatus_Data DeviceStatus;
struct _CD_GetDriver_Data Driver;
struct _CD_SectorSize_Data Sector;
struct _CD_GetHeadLoc_Data Head;
struct _CD_VolumeSize_Data Volume;
struct _CD_GetUPC_Data UPC;
} CD_Data;
#pragma pack()
Ogni struttura è composta da altre struttura ognuna con un nome che richiama direttamente al tipo di funzione svolta. Nella struttura CD_Param c'è una Signature comune ad ogni chiamata relativa ai CD-ROM, che deve contenere sempre la stringa "CD01" senza zero terminale (infatti sono quattro caratteri che formano appunto un ULONG e per inizializzarla si può usare la costante '10DC' ).
Usando l'union ogni struttura è sovrapposta a tutte le altre e quindi l'utilizzo di memoria è pari alla più grande delle strutture (nella prima c'è anche la signature). Bisogna però tener presente che se la struttura viene usata per chiamate diverse bisogna inizializzare solo la parte sovrascritta dalla chiamata precedente (ad esempio la Signature non viene mai sovrascritta e quindi può essere inizzializzata solo la prima volta che si usa la struttura).
È importante indicare la dimensione della struttura tramite l'operatore sizeof() in quanto calcolarsi a mano la dimensione della struttura può essere complicato.
Vediamo ora le singole strutture partendo da quelle relative ai parametri in ingresso e carcando di raggrupparle:
#define CDAUDIO_ADDRESS_LOGICAL 0x0000
#define CDAUDIO_ADDRESS_MSF 0x0001
#pragma pack (1)
struct _CD_Play_Param
{
UCHAR AddrMode;
ULONG StartSector;
ULONG EndSector;
};
struct _CD_Seek_Param
{
UCHAR AddrMode;
ULONG StartSector;
};
struct _CD_GetHeadLoc_Param
{
UCHAR AddrMode;
};
struct _CD_ReadLong_Param
{
UCHAR AddrMode;
USHORT Sectors;
ULONG StartSector;
UCHAR dummy;
UCHAR Interleave;
};
#pragma pack()
Tutte queste strutture sono relative a funzione che devono indicare delle zone del CD.
Per indicare un settore del CD si possono usare due metodi (questo spiega la presenza del campo AddrMode e delle due macro iniziali): indirizzi logici e indirizzi in minuti, secondi e frame (MSF). Gli indirizzi logici altro non sono che il numero progressivo del settore del CD considerando che il primo settore è il numero 150 (?). Gli indirizzzi MSF invece derivano direttamente dallo standard dei CD audio e possono essere facilmente essere convertiti in indirizzi logici se si considera che ogni frame corrisponde ad un settore e che ci sono 75 frames in ogni secondo e 60 secondi in ogni minuto. Nel formato MSF il byte meno significativo contiene il numero dei frames in binario, il byte successivo il numero di secondi e il terzo byte il numero di minuti (sempre in binario), il quarto byte deve essere zero.
L'ultima struttura contiene due campi, dummy e Interleave che non sono usati e devono essere messi a zero. Invece il campo Sectors serve per specificare il numero di settori lunghi 2352 bytes che deve essere letto.
struct _CD_TrackInfo_Param
{
UCHAR Track;
};
struct _CD_LockUnLock_Param
{
UCHAR Flag;
};
Queste due strutture sono molto semplici la prima contiene il numero della traccia in binario, la seconda se contiene il valore 00 sblocca il caricamento del CD se contiene 01 lo blocca.
Eccoci quindi alle strutture relative ai parametri restituiti dal device driver:
struct _CD_DiskInfo_Data
{
UCHAR FirstTrack;
UCHAR LastTrack;
ULONG LeadOutAddress;
};
struct _CD_TrackInfo_Data
{
ULONG TrackAddr;
UCHAR TrackCtrl;
};
struct _CD_GetHeadLoc_Data
{
ULONG Location;
};
La prima struttura contiene il numero della prima e dell'ultima traccia, insieme all'ultimo indirizzo valido del CD (in formato MSF).
La seconda struttura contiene l'indirizzo della traccia in formato MSF più un campo di bit che contiene informazioni sulla traccia:
TrackCtrl
Bit |
Descrizione |
7 |
0 = audio 2 canali 1 = audio 4 canali |
6 |
0 = traccia audio 1 = traccia dati |
5 |
0 = copia digitale proibita 1 = copia digitale permessa |
4 |
0 = audio senza preenfasi 1 = audio con preenfasi |
0-3 |
dati ADR della traccia |
L'ultima struttura contiene l'indirizzo della posizione della testina di lettura nel formato specificato dai parametri in ingresso.
Eccoci quindi ad una serie di strutture per avere informazioni sullo stato del driver e delle operaziini in corso:
struct _CD_AudioStatus_Data
{
USHORT Status;
ULONG Start;
ULONG End;
};
struct _CD_DeviceStatus_Data
{
ULONG Status;
};
struct _CD_GetDriver_Data
{
UCHAR Signature[4];
};
struct _CD_SectorSize_Data
{
USHORT Size;
};
struct _CD_VolumeSize_Data
{
ULONG Size;
};
struct _CD_GetUPC_Data
{
UCHAR Control;
UCHAR UPC[7];
UCHAR dummy;
UCHAR Frame;
};
La prima struttura fornisce informazioni sullo stato della lettura di tracce audio. Il primo campo è zero se non siamo in pause, altrimenti vale uno (è una mappa di bit di cui solo il primo è definito). Il secondo campo è l'indirizzo da cui ripartire, nel caso di pause, altrimenti è l'indirzzo di partenza dell'ultimo comando di play. Il terzo campo indica l'indirizzo di termine dell'ultimo play. Tutti gli indizzi sono in formato MSF.
La seconda struttura contiene un campo di bit che indica le varie funzionalità supportate dal device o dal drive:
Status
Bit |
Descrizione |
31 |
riservato; vale sempre 0 |
30 |
0 = Non è in grado di leggere settori CD-DA. 1 = è in grado di leggere settori CD-DA. |
13-29 |
Riservati; valgono sempre 0 |
12 |
0 = Non stà leggendo tracce audio. 1 = Stà leggendo tracce audio. |
11 |
0 = Disco inserito. 1 = Disco non inserito. |
10 |
0 = Non è in grado di leggere settori in Modo 2. 1 = è in grado di legger settori in Modo 2 |
9 |
0 = Accetta indirizzi logici. 1 = Accetta indirizzi logici e in MSF. |
8 |
0 = Non può elaborare il canale audio. 1 = Può elaborare il canale audio. |
7 |
0 = Prefetcing not supported. 1 = Prefetcing supprted internally. |
6 |
Riservato; vale sempre 0 |
5 |
0 = Non è ingrado di fare interleaving. 1 = Accetta l'interleaving ISO-9660 |
4 |
0 = Può leggere solo dati. 1 = Può leggere dati e musica. |
3 |
0 = Solo lettura 1 = Lettura e registrazione. |
2 |
0 = Legge solo settori di 2048 bytes. 1 = Legge settori di 2048 e 2352 bytes. |
1 |
0 = Cassetto bloccato 1 = Cassetto sbloccato. |
0 |
0 = Cassetto chiuso. 1 = Cassetto aperto. |
La terza struttura restituisce esattamente la "signature" del driver in modo da essere sicuri che si stà lavorando su un CD.
La quarta e la quinta struttura restituiscono rispettivamente la dimensione del settore (in byte) e la dimensione, in settori, dell'intero CD.
L'ultima struttura è utile sostanzialmente per ottenere l'Universal Product Code del CD all'interno del lettore. Gli altri campi sono di scarsa utilità (o almeno io non lho capita).
Questa struttura ci fornisce informazioni sulla posizione corrente della testina durante la lettura delle tracce audio:
struct _CD_SubchannelQ_Data
{
UCHAR Ctrl;
UCHAR Track;
UCHAR TrackMin;
UCHAR TrackSec;
UCHAR TrackFrm;
UCHAR dummy;
UCHAR DiskMin;
UCHAR DiskSec;
UCHAR DiskFrm;
};
Il primo campo contiene le stesse informazioni che si ottengono chiedendo i dati di una traccia. Gli altri campi, mi sembra che siano autoesplicativi. I valori sono tutti in binario.
Questa truttura serve sia per assegnare che per leggere i settaggi dei canali audio:
struct _CD_ChannelCtrl_Data
{
UCHAR InOut0;
UCHAR Volume0;
UCHAR InOut1;
UCHAR Volume1;
UCHAR InOut2;
UCHAR Volume2;
UCHAR InOut3;
UCHAR Volume3;
};
La struttura contiene due parametri per ciascuno dei quattro canali: il primo indica il numero del canale letto che deve essere fatto uscire il secondo indica il volume (00-FF hex).
Questa ultima struttura è utile per trattare i settori di tipo lungo (2352 bytes):
typedef struct _CD_Sector
{
UCHAR Sync[12];
UCHAR Header[4];
UCHAR Data[2048];
UCHAR ECC[288];
} CD_Sector;
Breve descrizione di ciascuna funzione
Tutte le chiamate al driver del CD necessitano come parametro in ingresso la signature "CD01" (quando scrivo "nessun parametro" sottintendo che la signature deve essere fornita ugualmente).
CD DevIOCtls
Funzione |
Commento |
Reset Drive |
Resetta il drive e il device driver. Nessun paramtro. |
Eject Disc |
Espelle il disco. Nesssun parametro. |
Close Tray |
Chiude il cassetto di caricamento del CD. Nessun parametro. |
Lock/Unlock Door |
Blocca/Sblocca l'apertura del cassetto. Necessita della struttura _CD_LockUnLock_Param. |
Seek |
Muove la testina di lettura fino alla posizione specificata. Richiede come parametro in ingresso la struttura _CD_Seek_Param. |
Device Status |
Restituisce lo stato del drive o device. Restituisce lo stato nella struttura _CD_DeviceStatus_Data |
Identify CD-ROM Driver |
Identifica il driver del CD. Restitisce la stringa di riconoscimento nella struttura _CD_GetDriver_Data |
Return Sector Size |
Restituisce la dimensione del settore nella struttura _CD_SectorSize_Data. |
Report Location of Drive Head |
Restituisce la posizione della testa di lettura nella struttura _CD_GetHeadLoc_Data nel formato specificato in _CD_GetHeadLoc_Param. |
Read Long |
Legge a partire dall'idirizzo indicato nella struttura _CD_ReadLong_Param un numero di settori specificato nella stessa struttura. Il formato dei settori letto è rappresentato dalla struttura _CD_Sector. Bisogna fornire un'opportuna zona di memoria per contenere tutti i settori letti. |
Return Volume Size |
Restituisce, nella struttura _CD_VolumeSize_Data, la dimensione in settori del CD nel lettore. |
Get UPC |
Compila la struttura _CD_GetUPC_Data con i dati relativi al Cd nel lettore. |
Set Audio-Channel Control |
Setta i canali audio in base all'informazioni nella struttura _CD_ChannelCtrl_Data. Attenzione che, questa struttura, pur essendo un dato in ingresso deve essere passata alla DosDevIOCtl come seconda struttura. |
Play Audio |
Legge la traccia audio in base ai parametri forniti in _CD_Play_Param. |
Stop Audio |
Interrompe la lettura audio (in realtà è una specie di pause). Nessun parametro. |
Resume Audio |
Riprende la lettura dove era stata interrotta con lo stop. Nessun parametro. |
Return Audio-Channel Information |
Compila la struttura _CD_ChannelCtrl_Data con i valori attuali. |
Return Audio-Disc Information |
Compila la struttura _CD_DiskInfo_Data. |
Return Audio-Track Information |
Compila la struttura _CD_TrackInfo_Data relativamente alla traccia specificata in _CD_TrackInfo_Param. |
Return Audio-Subchannel Q Information |
Compila la struttura _CD_SubchannelQ_Data. |
Return Audio-Status Information |
Compila la struttura _CD_AudioStatus_Data |
|