BIG REORGANIZATION.

Reorganize directory structure for inclusion of cd-paranoia. Works for
GNU/Linux. Other OS's may be broken. Regression test output needs to
be adjusted too.

Move:
lib/driver (split off of lib)
lib/iso9660 (split off of lib)

Add from paranoia:
lib/cdda_interface
lib/paranoia
src/paranoia

Also made some small changes to capability indentification to show
more reading capabilties and show that.

cd-info now shows the total disc size.
This commit is contained in:
rocky
2004-12-18 17:29:32 +00:00
parent a8f67b6163
commit 6c14d28918
109 changed files with 10863 additions and 329 deletions

View File

@@ -0,0 +1,23 @@
# $Id: Makefile,v 1.1 2004/12/18 17:29:32 rocky Exp $
#
# Copyright (C) 2004 Rocky Bernstein
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#######################################
# The make is done above. This boilerplate Makefile just transfers the call
all install check clean:
cd .. && $(MAKE) $@

View File

@@ -0,0 +1,805 @@
/*
$Id: aspi32.c,v 1.1 2004/12/18 17:29:32 rocky Exp $
Copyright (C) 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This file contains Win32-specific code and implements low-level
control of the CD drive via the ASPI API.
Inspired by vlc's cdrom.h code
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static const char _rcsid[] = "$Id: aspi32.c,v 1.1 2004/12/18 17:29:32 rocky Exp $";
#include <cdio/cdio.h>
#include <cdio/sector.h>
#include <cdio/util.h>
#include <cdio/scsi_mmc.h>
#include "cdio_assert.h"
#include <string.h>
#ifdef HAVE_WIN32_CDROM
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <windows.h>
#include "win32.h"
#include <sys/stat.h>
#include <sys/types.h>
#include "aspi32.h"
#include "cdtext_private.h"
/* Amount of time we are willing to wait for an operation to complete.
10 seconds?
*/
#define OP_TIMEOUT_MS 10000
static const
char *aspierror(int nErrorCode)
{
switch (nErrorCode)
{
case SS_PENDING:
return "SRB being processed";
break;
case SS_COMP:
return "SRB completed without error";
break;
case SS_ABORTED:
return "SRB aborted";
break;
case SS_ABORT_FAIL:
return "Unable to abort SRB";
break;
case SS_ERR:
return "SRB completed with error";
break;
case SS_INVALID_CMD:
return "Invalid ASPI command";
break;
case SS_INVALID_HA:
return "Invalid host adapter number";
break;
case SS_NO_DEVICE:
return "SCSI device not installed";
break;
case SS_INVALID_SRB:
return "Invalid parameter set in SRB";
break;
case SS_OLD_MANAGER:
return "ASPI manager doesn't support";
break;
case SS_ILLEGAL_MODE:
return "Unsupported MS Windows mode";
break;
case SS_NO_ASPI:
return "No ASPI managers";
break;
case SS_FAILED_INIT:
return "ASPI for windows failed init";
break;
case SS_ASPI_IS_BUSY:
return "No resources available to execute command.";
break;
case SS_BUFFER_TOO_BIG:
return "Buffer size is too big to handle.";
break;
case SS_MISMATCHED_COMPONENTS:
return "The DLLs/EXEs of ASPI don't version check";
break;
case SS_NO_ADAPTERS:
return "No host adapters found";
break;
case SS_INSUFFICIENT_RESOURCES:
return "Couldn't allocate resources needed to init";
break;
case SS_ASPI_IS_SHUTDOWN:
return "Call came to ASPI after PROCESS_DETACH";
break;
case SS_BAD_INSTALL:
return "The DLL or other components are installed wrong.";
break;
default:
return "Unknown ASPI error.";
}
}
/* General ioctl() CD-ROM command function */
static bool
mciSendCommand_aspi(int id, UINT msg, DWORD flags, void *arg)
{
MCIERROR mci_error;
mci_error = mciSendCommand(id, msg, flags, (DWORD)arg);
if ( mci_error ) {
char error[256];
mciGetErrorString(mci_error, error, 256);
cdio_warn("mciSendCommand() error: %s", error);
}
return(mci_error == 0);
}
/*
See if the ASPI DLL is loadable. If so pointers are returned
and we return true. Return false if there was a problem.
*/
static bool
have_aspi( HMODULE *hASPI,
long (**lpGetSupport)( void ),
long (**lpSendCommand)( void* ) )
{
/* check if aspi is available */
*hASPI = LoadLibrary( "wnaspi32.dll" );
if( *hASPI == NULL ) {
cdio_debug("Unable to load ASPI DLL");
return false;
}
(FARPROC) *lpGetSupport = GetProcAddress( *hASPI,
"GetASPI32SupportInfo" );
(FARPROC) *lpSendCommand = GetProcAddress( *hASPI,
"SendASPI32Command" );
/* make sure that we've got both function addresses */
if( *lpGetSupport == NULL || *lpSendCommand == NULL ) {
cdio_debug("Unable to get ASPI function pointers");
FreeLibrary( *hASPI );
return false;
}
return true;
}
/*!
Get disc type associated with cd object.
*/
discmode_t
get_discmode_aspi (_img_private_t *p_env)
{
track_t i_track;
discmode_t discmode=CDIO_DISC_MODE_NO_INFO;
/* See if this is a DVD. */
cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */
dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL;
dvd.physical.layer_num = 0;
if (0 == scsi_mmc_get_dvd_struct_physical_private (p_env,
&run_scsi_cmd_aspi,
&dvd)) {
switch(dvd.physical.layer[0].book_type) {
case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM;
case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM;
case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R;
case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW;
case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR;
case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW;
default: return CDIO_DISC_MODE_DVD_OTHER;
}
}
if (!p_env->gen.toc_init)
read_toc_aspi (p_env);
if (!p_env->gen.toc_init)
return CDIO_DISC_MODE_NO_INFO;
for (i_track = p_env->gen.i_first_track;
i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ;
i_track ++) {
track_format_t track_fmt=get_track_format_aspi(p_env, i_track);
switch(track_fmt) {
case TRACK_FORMAT_AUDIO:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_DA;
break;
case CDIO_DISC_MODE_CD_DA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_XA:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_XA;
break;
case CDIO_DISC_MODE_CD_XA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_DATA:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_DATA;
break;
case CDIO_DISC_MODE_CD_DATA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_ERROR:
default:
discmode = CDIO_DISC_MODE_ERROR;
}
}
return discmode;
}
const char *
is_cdrom_aspi(const char drive_letter)
{
static char psz_win32_drive[7];
HMODULE hASPI = NULL;
long (*lpGetSupport)( void ) = NULL;
long (*lpSendCommand)( void* ) = NULL;
DWORD dwSupportInfo;
int i_adapter, i_hostadapters;
char c_drive;
int i_rc;
if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) )
return NULL;
/* ASPI support seems to be there. */
dwSupportInfo = lpGetSupport();
i_rc = HIBYTE( LOWORD ( dwSupportInfo ) );
if( SS_COMP != i_rc ) {
cdio_debug("ASPI: %s", aspierror(i_rc));
FreeLibrary( hASPI );
return NULL;
}
i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
if( i_hostadapters == 0 ) {
FreeLibrary( hASPI );
return NULL;
}
c_drive = toupper(drive_letter) - 'A';
for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) {
struct SRB_GetDiskInfo srbDiskInfo;
int i_target;
SRB_HAInquiry srbInquiry;
srbInquiry.SRB_Cmd = SC_HA_INQUIRY;
srbInquiry.SRB_HaId = i_adapter;
lpSendCommand( (void*) &srbInquiry );
if( srbInquiry.SRB_Status != SS_COMP ) continue;
if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8;
for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++)
{
int i_lun;
for( i_lun=0; i_lun<8; i_lun++)
{
srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO;
srbDiskInfo.SRB_Flags = 0;
srbDiskInfo.SRB_Hdr_Rsvd = 0;
srbDiskInfo.SRB_HaId = i_adapter;
srbDiskInfo.SRB_Target = i_target;
srbDiskInfo.SRB_Lun = i_lun;
lpSendCommand( (void*) &srbDiskInfo );
if( (srbDiskInfo.SRB_Status == SS_COMP) &&
(srbDiskInfo.SRB_Int13HDriveInfo == c_drive) ) {
/* Make sure this is a CD-ROM device. */
struct SRB_GDEVBlock srbGDEVBlock;
memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) );
srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
srbDiskInfo.SRB_HaId = i_adapter;
srbGDEVBlock.SRB_Target = i_target;
srbGDEVBlock.SRB_Lun = i_lun;
lpSendCommand( (void*) &srbGDEVBlock );
if( ( srbGDEVBlock.SRB_Status == SS_COMP ) &&
( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) {
sprintf( psz_win32_drive, "%c:", drive_letter );
FreeLibrary( hASPI );
return(psz_win32_drive);
}
}
}
}
}
FreeLibrary( hASPI );
return NULL;
}
/*!
Initialize CD device.
*/
bool
init_aspi (_img_private_t *env)
{
HMODULE hASPI = NULL;
long (*lpGetSupport)( void ) = NULL;
long (*lpSendCommand)( void* ) = NULL;
DWORD dwSupportInfo;
int i_adapter, i_hostadapters;
char c_drive;
int i_rc;
if (2 == strlen(env->gen.source_name) && isalpha(env->gen.source_name[0]) )
{
c_drive = env->gen.source_name[0];
} else if ( 6 == strlen(env->gen.source_name)
&& isalpha(env->gen.source_name[4] )) {
c_drive = env->gen.source_name[4];
} else {
c_drive = 'C';
}
if ( !have_aspi(&hASPI, &lpGetSupport, &lpSendCommand) )
return false;
/* ASPI support seems to be there. */
dwSupportInfo = lpGetSupport();
i_rc = HIBYTE( LOWORD ( dwSupportInfo ) );
if( SS_COMP != i_rc ) {
cdio_info("ASPI: %s", aspierror(i_rc));
FreeLibrary( hASPI );
return false;
}
i_hostadapters = LOBYTE( LOWORD( dwSupportInfo ) );
if( i_hostadapters == 0 ) {
FreeLibrary( hASPI );
return false;
}
c_drive = toupper(c_drive) - 'A';
for( i_adapter = 0; i_adapter < i_hostadapters; i_adapter++ ) {
struct SRB_GetDiskInfo srbDiskInfo;
int i_target;
SRB_HAInquiry srbInquiry;
srbInquiry.SRB_Cmd = SC_HA_INQUIRY;
srbInquiry.SRB_HaId = i_adapter;
lpSendCommand( (void*) &srbInquiry );
if( srbInquiry.SRB_Status != SS_COMP ) continue;
if( !srbInquiry.HA_Unique[3]) srbInquiry.HA_Unique[3]=8;
for(i_target=0; i_target < srbInquiry.HA_Unique[3]; i_target++)
{
int i_lun;
for (i_lun = 0; i_lun < 8; i_lun++ ) {
srbDiskInfo.SRB_Cmd = SC_GET_DISK_INFO;
srbDiskInfo.SRB_Flags = 0;
srbDiskInfo.SRB_Hdr_Rsvd = 0;
srbDiskInfo.SRB_HaId = i_adapter;
srbDiskInfo.SRB_Target = i_target;
srbDiskInfo.SRB_Lun = i_lun;
lpSendCommand( (void*) &srbDiskInfo );
if( (srbDiskInfo.SRB_Status == SS_COMP) ) {
if (srbDiskInfo.SRB_Int13HDriveInfo != c_drive)
{
continue;
} else {
/* Make sure this is a CD-ROM device. */
struct SRB_GDEVBlock srbGDEVBlock;
memset( &srbGDEVBlock, 0, sizeof(struct SRB_GDEVBlock) );
srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
srbGDEVBlock.SRB_HaId = i_adapter;
srbGDEVBlock.SRB_Target = i_target;
lpSendCommand( (void*) &srbGDEVBlock );
if( ( srbGDEVBlock.SRB_Status == SS_COMP ) &&
( srbGDEVBlock.SRB_DeviceType == DTYPE_CDROM ) ) {
env->i_sid = MAKEWORD( i_adapter, i_target );
env->hASPI = (long)hASPI;
env->lpSendCommand = lpSendCommand;
env->b_aspi_init = true;
env->i_lun = i_lun;
cdio_debug("Using ASPI layer");
return true;
} else {
FreeLibrary( hASPI );
cdio_debug( "%c: is not a CD-ROM drive",
env->gen.source_name[0] );
return false;
}
}
}
}
}
}
FreeLibrary( hASPI );
cdio_debug( "Unable to get HaId and target (ASPI)" );
return false;
}
/*!
Run a SCSI MMC command.
env private CD structure
i_timeout_ms time in milliseconds we will wait for the command
to complete. If this value is -1, use the default
time-out value.
p_buf Buffer for data, both sending and receiving
i_buf Size of buffer
e_direction direction the transfer is to go.
cdb CDB bytes. All values that are needed should be set on
input. We'll figure out what the right CDB length should be.
We return 0 if command completed successfully.
*/
int
run_scsi_cmd_aspi( const void *p_user_data, unsigned int i_timeout_ms,
unsigned int i_cdb, const scsi_mmc_cdb_t * p_cdb,
scsi_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf )
{
const _img_private_t *p_env = p_user_data;
HANDLE hEvent;
struct SRB_ExecSCSICmd ssc;
/* Create the transfer completion event */
hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if( hEvent == NULL ) {
cdio_info("CreateEvent failed");
return 1;
}
memset( &ssc, 0, sizeof( ssc ) );
ssc.SRB_Cmd = SC_EXEC_SCSI_CMD;
ssc.SRB_Flags = SCSI_MMC_DATA_READ == e_direction ?
SRB_DIR_IN | SRB_EVENT_NOTIFY : SRB_DIR_OUT | SRB_EVENT_NOTIFY;
ssc.SRB_HaId = LOBYTE( p_env->i_sid );
ssc.SRB_Target = HIBYTE( p_env->i_sid );
ssc.SRB_Lun = p_env->i_lun;
ssc.SRB_SenseLen = SENSE_LEN;
ssc.SRB_PostProc = (LPVOID) hEvent;
ssc.SRB_CDBLen = i_cdb;
/* Result buffer */
ssc.SRB_BufPointer = p_buf;
ssc.SRB_BufLen = i_buf;
memcpy( ssc.CDBByte, p_cdb, i_cdb );
ResetEvent( hEvent );
p_env->lpSendCommand( (void*) &ssc );
/* If the command has still not been processed, wait until it's
* finished */
if( ssc.SRB_Status == SS_PENDING ) {
WaitForSingleObject( hEvent, msecs2secs(i_timeout_ms) );
}
CloseHandle( hEvent );
/* check that the transfer went as planned */
if( ssc.SRB_Status != SS_COMP ) {
cdio_info("ASPI: %s", aspierror(ssc.SRB_Status));
return 2;
}
return 0;
}
/*!
Reads nblocks sectors from cd device into data starting from lsn.
Returns 0 if no error.
*/
static int
read_sectors_aspi (const _img_private_t *env, void *data, lsn_t lsn,
int sector_type, unsigned int nblocks)
{
scsi_mmc_cdb_t cdb = {{0, }};
unsigned int i_buf;
int sync = 0;
int header_code = 2;
int i_user_data = 1;
int edc_ecc = 0;
int error_field = 0;
#if 0
sector_type = 0; /*all types */
#endif
/* Set up passthrough command */
CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD);
CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type);
CDIO_MMC_SET_READ_LBA (cdb.field, lsn);
CDIO_MMC_SET_READ_LENGTH24(cdb.field, nblocks);
#if 1
cdb.field[ 9 ] = (sync << 7) |
(header_code << 5) |
(i_user_data << 4) |
(edc_ecc << 3) |
(error_field << 1);
/* ssc.CDBByte[ 9 ] = READ_CD_USERDATA_MODE2; */
#else
CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cmd,
CDIO_MMC_MCSB_ALL_HEADERS);
#endif
switch (sector_type) {
case CDIO_MMC_READ_TYPE_ANY:
case CDIO_MMC_READ_TYPE_CDDA:
i_buf = CDIO_CD_FRAMESIZE_RAW;
break;
case CDIO_MMC_READ_TYPE_M2F1:
i_buf = CDIO_CD_FRAMESIZE;
break;
case CDIO_MMC_READ_TYPE_M2F2:
i_buf = 2324;
break;
case CDIO_MMC_READ_TYPE_MODE1:
i_buf = CDIO_CD_FRAMESIZE;
break;
default:
i_buf = CDIO_CD_FRAMESIZE_RAW;
}
return run_scsi_cmd_aspi(env, OP_TIMEOUT_MS,
scsi_mmc_get_cmd_len(cdb.field[0]),
&cdb, SCSI_MMC_DATA_READ, i_buf*nblocks, data);
}
/*!
Reads an audio device into data starting from lsn.
Returns 0 if no error.
*/
int
read_audio_sectors_aspi (_img_private_t *env, void *data, lsn_t lsn,
unsigned int nblocks)
{
if (read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_CDDA, 1)) {
return read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_ANY, 1);
}
return 0;
}
/*!
Reads a single mode2 sector from cd device into data starting
from lsn. Returns 0 if no error.
*/
int
read_mode2_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn,
bool b_form2)
{
return read_sectors_aspi(env, data, lsn, b_form2
? CDIO_MMC_READ_TYPE_M2F2
: CDIO_MMC_READ_TYPE_M2F1,
1);
}
/*!
Reads a single mode2 sector from cd device into data starting
from lsn. Returns 0 if no error.
*/
int
read_mode1_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn,
bool b_form2)
{
return read_sectors_aspi(env, data, lsn, CDIO_MMC_READ_TYPE_MODE1, 1);
}
/*!
Read and cache the CD's Track Table of Contents and track info.
Return true if successful or false if an error.
*/
bool
read_toc_aspi (_img_private_t *p_env)
{
scsi_mmc_cdb_t cdb = {{0, }};
unsigned char tocheader[ 4 ];
int i_status;
/* Operation code */
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
/* Format */
cdb.field[ 2 ] = CDIO_MMC_READTOC_FMT_TOC;
/* Starting track */
CDIO_MMC_SET_START_TRACK(cdb.field, 0);
CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(tocheader));
i_status = run_scsi_cmd_aspi (p_env, OP_TIMEOUT_MS,
scsi_mmc_get_cmd_len(cdb.field[0]),
&cdb, SCSI_MMC_DATA_READ,
sizeof(tocheader), &tocheader);
if (0 != i_status) return false;
p_env->gen.i_first_track = tocheader[2];
p_env->gen.i_tracks = tocheader[3] - tocheader[2] + 1;
{
int i, i_toclength;
unsigned char *p_fulltoc;
i_toclength = 4 /* header */ + tocheader[0] +
((unsigned int) tocheader[1] << 8);
p_fulltoc = malloc( i_toclength );
if( p_fulltoc == NULL ) {
cdio_error( "out of memory" );
return false;
}
CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_toclength);
i_status = run_scsi_cmd_aspi (p_env, OP_TIMEOUT_MS,
scsi_mmc_get_cmd_len(cdb.field[0]),
&cdb, SCSI_MMC_DATA_READ,
i_toclength, p_fulltoc);
if( 0 != i_status ) {
p_env->gen.i_tracks = 0;
}
for( i = 0 ; i <= p_env->gen.i_tracks ; i++ ) {
int i_index = 8 + 8 * i;
p_env->tocent[ i ].start_lsn = ((int)p_fulltoc[ i_index ] << 24) +
((int)p_fulltoc[ i_index+1 ] << 16) +
((int)p_fulltoc[ i_index+2 ] << 8) +
(int)p_fulltoc[ i_index+3 ];
p_env->tocent[ i ].Control = (UCHAR)p_fulltoc[ 1 + 8 * i ];
cdio_debug( "p_sectors: %i %lu",
i, (unsigned long int) p_env->tocent[i].start_lsn );
}
free( p_fulltoc );
}
p_env->gen.toc_init = true;
return true;
}
/* Eject media will eventually get removed from _cdio_win32.c */
#if 0
/*!
Eject media. Return 1 if successful, 0 otherwise.
*/
int
wnaspi32_eject_media (void *user_data) {
_img_private_t *env = user_data;
MCI_OPEN_PARMS op;
MCI_STATUS_PARMS st;
DWORD i_flags;
char psz_drive[4];
int ret;
memset( &op, 0, sizeof(MCI_OPEN_PARMS) );
op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
strcpy( psz_drive, "X:" );
psz_drive[0] = env->gen.source_name[0];
op.lpstrElementName = psz_drive;
/* Set the flags for the device type */
i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE;
if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) {
st.dwItem = MCI_STATUS_READY;
/* Eject disc */
ret = mciSendCommand_aspi( op.wDeviceID, MCI_SET,
MCI_SET_DOOR_OPEN, 0 ) != 0;
/* Release access to the device */
mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 );
} else
ret = 0;
return ret;
}
#endif
/*!
Get format of track.
*/
track_format_t
get_track_format_aspi(const _img_private_t *p_env, track_t track_num)
{
MCI_OPEN_PARMS op;
MCI_STATUS_PARMS st;
DWORD i_flags;
int ret;
memset( &op, 0, sizeof(MCI_OPEN_PARMS) );
op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
op.lpstrElementName = p_env->gen.source_name;
/* Set the flags for the device type */
i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE;
if( mciSendCommand_aspi( 0, MCI_OPEN, i_flags, &op ) ) {
st.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
st.dwTrack = track_num;
i_flags = MCI_TRACK | MCI_STATUS_ITEM ;
ret = mciSendCommand_aspi( op.wDeviceID, MCI_STATUS, i_flags, &st );
/* Release access to the device */
mciSendCommand_aspi( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 );
switch(st.dwReturn) {
case MCI_CDA_TRACK_AUDIO:
return TRACK_FORMAT_AUDIO;
case MCI_CDA_TRACK_OTHER:
return TRACK_FORMAT_DATA;
default:
return TRACK_FORMAT_XA;
}
}
return TRACK_FORMAT_ERROR;
}
#endif /* HAVE_WIN32_CDROM */

View File

@@ -0,0 +1,249 @@
/* Win32 aspi specific */
/*
$Id: aspi32.h,v 1.1 2004/12/18 17:29:32 rocky Exp $
Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#define ASPI_HAID 0
#define ASPI_TARGET 0
#define DTYPE_CDROM 0x05
#define SENSE_LEN 0x0E
#define SC_HA_INQUIRY 0x00
#define SC_GET_DEV_TYPE 0x01
#define SC_EXEC_SCSI_CMD 0x02
#define SC_GET_DISK_INFO 0x06
//*****************************************************************************
// %%% SRB Status %%%
//*****************************************************************************
#define SS_PENDING 0x00 // SRB being processed
#define SS_COMP 0x01 // SRB completed without error
#define SS_ABORTED 0x02 // SRB aborted
#define SS_ABORT_FAIL 0x03 // Unable to abort SRB
#define SS_ERR 0x04 // SRB completed with error
#define SS_INVALID_CMD 0x80 // Invalid ASPI command
#define SS_INVALID_HA 0x81 // Invalid host adapter number
#define SS_NO_DEVICE 0x82 // SCSI device not installed
#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB
#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows
#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces
// OLD_MANAGER in Win32)
#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode
#define SS_NO_ASPI 0xE3 // No ASPI managers resident
#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init
#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute
// cmd
#define SS_BUFFER_TOO_BIG 0xE6 // Buffer size to big to handle!
#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version
// check
#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage
#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed
// to init
#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after
// PROCESS_DETACH
#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong
//*****************************************************************************
// %%% Host Adapter Status %%%
//*****************************************************************************
#define HASTAT_OK 0x00 // Host adapter did not detect an
// error
#define HASTAT_SEL_TO 0x11 // Selection Timeout
#define HASTAT_DO_DU 0x12 // Data overrun data underrun
#define HASTAT_BUS_FREE 0x13 // Unexpected bus free
#define HASTAT_PHASE_ERR 0x14 // Target bus phase sequence
// failure
#define HASTAT_TIMEOUT 0x09 // Timed out while SRB was
// waiting to beprocessed.
#define HASTAT_COMMAND_TIMEOUT 0x0B // Adapter timed out processing SRB.
#define HASTAT_MESSAGE_REJECT 0x0D // While processing SRB, the
// adapter received a MESSAGE
#define HASTAT_BUS_RESET 0x0E // A bus reset was detected.
#define HASTAT_PARITY_ERROR 0x0F // A parity error was detected.
#define HASTAT_REQUEST_SENSE_FAILED 0x10 // The adapter failed in issuing
#define SS_NO_ADAPTERS 0xE8
#define SRB_DIR_IN 0x08
#define SRB_DIR_OUT 0x10
#define SRB_EVENT_NOTIFY 0x40
#define SECTOR_TYPE_MODE2 0x14
#define READ_CD_USERDATA_MODE2 0x10
#define READ_TOC 0x43
#define READ_TOC_FORMAT_TOC 0x0
#pragma pack(1)
struct SRB_GetDiskInfo
{
unsigned char SRB_Cmd;
unsigned char SRB_Status;
unsigned char SRB_HaId;
unsigned char SRB_Flags;
unsigned long SRB_Hdr_Rsvd;
unsigned char SRB_Target;
unsigned char SRB_Lun;
unsigned char SRB_DriveFlags;
unsigned char SRB_Int13HDriveInfo;
unsigned char SRB_Heads;
unsigned char SRB_Sectors;
unsigned char SRB_Rsvd1[22];
};
struct SRB_GDEVBlock
{
unsigned char SRB_Cmd;
unsigned char SRB_Status;
unsigned char SRB_HaId;
unsigned char SRB_Flags;
unsigned long SRB_Hdr_Rsvd;
unsigned char SRB_Target;
unsigned char SRB_Lun;
unsigned char SRB_DeviceType;
unsigned char SRB_Rsvd1;
};
struct SRB_ExecSCSICmd
{
unsigned char SRB_Cmd;
unsigned char SRB_Status;
unsigned char SRB_HaId;
unsigned char SRB_Flags;
unsigned long SRB_Hdr_Rsvd;
unsigned char SRB_Target;
unsigned char SRB_Lun;
unsigned short SRB_Rsvd1;
unsigned long SRB_BufLen;
unsigned char *SRB_BufPointer;
unsigned char SRB_SenseLen;
unsigned char SRB_CDBLen;
unsigned char SRB_HaStat;
unsigned char SRB_TargStat;
unsigned long *SRB_PostProc;
unsigned char SRB_Rsvd2[20];
unsigned char CDBByte[16];
unsigned char SenseArea[SENSE_LEN+2];
};
/*****************************************************************************
%%% SRB - HOST ADAPTER INQUIRY - SC_HA_INQUIRY (0) %%%
*****************************************************************************/
typedef struct // Offset
{ // HX/DEC
BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY
BYTE SRB_Status; // 01/001 ASPI command status byte
BYTE SRB_HaId; // 02/002 ASPI host adapter number
BYTE SRB_Flags; // 03/003 ASPI request flags
DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0
BYTE HA_Count; // 08/008 Number of host adapters present
BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter
BYTE HA_ManagerId[16]; // 0A/010 String describing the manager
BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter
BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters
WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0
}
SRB_HAInquiry;
/*!
Get disc type associated with cd object.
*/
discmode_t get_discmode_aspi (_img_private_t *p_env);
/*!
Return the the kind of drive capabilities of device.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
char * get_mcn_aspi (const _img_private_t *env);
/*!
Get the format (XA, DATA, AUDIO) of a track.
*/
track_format_t get_track_format_aspi(const _img_private_t *env,
track_t i_track);
/*!
Initialize internal structures for CD device.
*/
bool init_aspi (_img_private_t *env);
/*
Read cdtext information for a CdIo object .
return true on success, false on error or CD-TEXT information does
not exist.
*/
bool init_cdtext_aspi (_img_private_t *env);
const char *is_cdrom_aspi(const char drive_letter);
/*!
Reads an audio device using the DeviceIoControl method into data
starting from lsn. Returns 0 if no error.
*/
int read_audio_sectors_aspi (_img_private_t *obj, void *data, lsn_t lsn,
unsigned int nblocks);
/*!
Reads a single mode1 sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
int read_mode1_sector_aspi (const _img_private_t *env, void *data,
lsn_t lsn, bool b_form2);
/*!
Reads a single mode2 sector from cd device into data starting
from lsn. Returns 0 if no error.
*/
int read_mode2_sector_aspi (const _img_private_t *env, void *data, lsn_t lsn,
bool b_form2);
/*!
Read and cache the CD's Track Table of Contents and track info.
Return true if successful or false if an error.
*/
bool read_toc_aspi (_img_private_t *env);
/*!
Run a SCSI MMC command.
env private CD structure
i_timeout time in milliseconds we will wait for the command
to complete. If this value is -1, use the default
time-out value.
p_buf Buffer for data, both sending and receiving
i_buf Size of buffer
e_direction direction the transfer is to go.
cdb CDB bytes. All values that are needed should be set on
input. We'll figure out what the right CDB length should be.
Return 0 if command completed successfully.
*/
int run_scsi_cmd_aspi( const void *p_user_data,
unsigned int i_timeout,
unsigned int i_cdb,
const scsi_mmc_cdb_t * p_cdb,
scsi_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf );

View File

@@ -0,0 +1,805 @@
/*
$Id: win32.c,v 1.1 2004/12/18 17:29:32 rocky Exp $
Copyright (C) 2003, 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This file contains Win32-specific code and implements low-level
control of the CD drive. Inspired by vlc's cdrom.h code
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static const char _rcsid[] = "$Id: win32.c,v 1.1 2004/12/18 17:29:32 rocky Exp $";
#include <cdio/cdio.h>
#include <cdio/sector.h>
#include <cdio/util.h>
#include <cdio/scsi_mmc.h>
#include "cdio_assert.h"
#include "cdio_private.h" /* protoype for cdio_is_device_win32 */
#include <string.h>
#ifdef HAVE_WIN32_CDROM
#include <ctype.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <windows.h>
#include <winioctl.h>
#include "win32.h"
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if defined (MSVC) || defined (_XBOX)
#undef IN
#else
#include "aspi32.h"
#endif
#ifdef _XBOX
#include "stdint.h"
#include <xtl.h>
#define WIN_NT 1
#else
#define WIN_NT ( GetVersion() < 0x80000000 )
#endif
/* General ioctl() CD-ROM command function */
static bool
_cdio_mciSendCommand(int id, UINT msg, DWORD flags, void *arg)
{
#ifdef _XBOX
return false;
#else
MCIERROR mci_error;
mci_error = mciSendCommand(id, msg, flags, (DWORD)arg);
if ( mci_error ) {
char error[256];
mciGetErrorString(mci_error, error, 256);
cdio_warn("mciSendCommand() error: %s", error);
}
return(mci_error == 0);
#endif
}
static access_mode_t
str_to_access_mode_win32(const char *psz_access_mode)
{
const access_mode_t default_access_mode =
WIN_NT ? _AM_IOCTL : _AM_ASPI;
if (NULL==psz_access_mode) return default_access_mode;
if (!strcmp(psz_access_mode, "ioctl"))
return _AM_IOCTL;
else if (!strcmp(psz_access_mode, "ASPI")) {
#ifdef _XBOX
return _AM_ASPI;
#else
cdio_warn ("XBOX doesn't support access type: %s. Default used instead.",
psz_access_mode);
return default_access_mode;
#endif
} else {
cdio_warn ("unknown access type: %s. Default used instead.",
psz_access_mode);
return default_access_mode;
}
}
static discmode_t
get_discmode_win32(void *p_user_data)
{
_img_private_t *p_env = p_user_data;
if (p_env->hASPI) {
return get_discmode_aspi (p_env);
} else {
return get_discmode_win32ioctl (p_env);
}
}
static const char *
is_cdrom_win32(const char drive_letter) {
if ( WIN_NT ) {
return is_cdrom_win32ioctl (drive_letter);
} else {
return is_cdrom_aspi(drive_letter);
}
}
/*!
Run a SCSI MMC command.
env private CD structure
i_timeout_ms time in milliseconds we will wait for the command
to complete. If this value is -1, use the default
time-out value.
p_buf Buffer for data, both sending and receiving
i_buf Size of buffer
e_direction direction the transfer is to go.
cdb CDB bytes. All values that are needed should be set on
input. We'll figure out what the right CDB length should be.
Return 0 if command completed successfully.
*/
static int
run_scsi_cmd_win32( const void *p_user_data, unsigned int i_timeout_ms,
unsigned int i_cdb, const scsi_mmc_cdb_t *p_cdb,
scsi_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf )
{
const _img_private_t *p_env = p_user_data;
if (p_env->hASPI) {
return run_scsi_cmd_aspi( p_env, i_timeout_ms, i_cdb, p_cdb,
e_direction, i_buf, p_buf );
} else {
return run_scsi_cmd_win32ioctl( p_env, i_timeout_ms, i_cdb, p_cdb,
e_direction, i_buf, p_buf );
}
}
/*!
Initialize CD device.
*/
static bool
_cdio_init_win32 (void *user_data)
{
_img_private_t *p_env = user_data;
if (p_env->gen.init) {
cdio_error ("init called more than once");
return false;
}
p_env->gen.init = true;
p_env->gen.toc_init = false;
p_env->gen.b_cdtext_init = false;
p_env->gen.b_cdtext_error = false;
/* Initializations */
p_env->h_device_handle = NULL;
p_env->i_sid = 0;
p_env->hASPI = 0;
p_env->lpSendCommand = 0;
p_env->b_aspi_init = false;
p_env->b_ioctl_init = false;
if ( _AM_IOCTL == p_env->access_mode ) {
return init_win32ioctl(p_env);
} else {
return init_aspi(p_env);
}
}
/*!
Release and free resources associated with cd.
*/
static void
_free_win32 (void *user_data)
{
_img_private_t *p_env = user_data;
if (NULL == p_env) return;
free (p_env->gen.source_name);
if( p_env->h_device_handle )
CloseHandle( p_env->h_device_handle );
if( p_env->hASPI )
FreeLibrary( (HMODULE)p_env->hASPI );
free (p_env);
}
/*!
Reads an audio device into data starting from lsn.
Returns 0 if no error.
*/
static int
_cdio_read_audio_sectors (void *user_data, void *data, lsn_t lsn,
unsigned int nblocks)
{
_img_private_t *p_env = user_data;
if ( p_env->hASPI ) {
return read_audio_sectors_aspi( p_env, data, lsn, nblocks );
} else {
return read_audio_sectors_win32ioctl( p_env, data, lsn, nblocks );
}
}
/*!
Reads a single mode1 sector from cd device into data starting
from lsn. Returns 0 if no error.
*/
static int
_cdio_read_mode1_sector (void *user_data, void *data, lsn_t lsn,
bool b_form2)
{
_img_private_t *p_env = user_data;
if (p_env->gen.ioctls_debugged == 75)
cdio_debug ("only displaying every 75th ioctl from now on");
if (p_env->gen.ioctls_debugged == 30 * 75)
cdio_debug ("only displaying every 30*75th ioctl from now on");
if (p_env->gen.ioctls_debugged < 75
|| (p_env->gen.ioctls_debugged < (30 * 75)
&& p_env->gen.ioctls_debugged % 75 == 0)
|| p_env->gen.ioctls_debugged % (30 * 75) == 0)
cdio_debug ("reading %lu", (unsigned long int) lsn);
p_env->gen.ioctls_debugged++;
if ( p_env->hASPI ) {
return read_mode1_sector_aspi( p_env, data, lsn, b_form2 );
} else {
return read_mode1_sector_win32ioctl( p_env, data, lsn, b_form2 );
}
}
/*!
Reads nblocks of mode1 sectors from cd device into data starting
from lsn.
Returns 0 if no error.
*/
static int
_cdio_read_mode1_sectors (void *user_data, void *data, lsn_t lsn,
bool b_form2, unsigned int nblocks)
{
_img_private_t *p_env = user_data;
int i;
int retval;
for (i = 0; i < nblocks; i++) {
if (b_form2) {
if ( (retval = _cdio_read_mode1_sector (p_env,
((char *)data) + (M2RAW_SECTOR_SIZE * i),
lsn + i, true)) )
return retval;
} else {
char buf[M2RAW_SECTOR_SIZE] = { 0, };
if ( (retval = _cdio_read_mode1_sector (p_env, buf, lsn + i, false)) )
return retval;
memcpy (((char *)data) + (CDIO_CD_FRAMESIZE * i),
buf, CDIO_CD_FRAMESIZE);
}
}
return 0;
}
/*!
Reads a single mode2 sector from cd device into data starting
from lsn. Returns 0 if no error.
*/
static int
_cdio_read_mode2_sector (void *user_data, void *data, lsn_t lsn,
bool b_form2)
{
char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
_img_private_t *p_env = user_data;
if (p_env->gen.ioctls_debugged == 75)
cdio_debug ("only displaying every 75th ioctl from now on");
if (p_env->gen.ioctls_debugged == 30 * 75)
cdio_debug ("only displaying every 30*75th ioctl from now on");
if (p_env->gen.ioctls_debugged < 75
|| (p_env->gen.ioctls_debugged < (30 * 75)
&& p_env->gen.ioctls_debugged % 75 == 0)
|| p_env->gen.ioctls_debugged % (30 * 75) == 0)
cdio_debug ("reading %lu", (unsigned long int) lsn);
p_env->gen.ioctls_debugged++;
if ( p_env->hASPI ) {
int ret;
ret = read_mode2_sector_aspi(user_data, buf, lsn, 1);
if( ret != 0 ) return ret;
if (b_form2)
memcpy (data, buf, M2RAW_SECTOR_SIZE);
else
memcpy (((char *)data), buf + CDIO_CD_SUBHEADER_SIZE, CDIO_CD_FRAMESIZE);
return 0;
} else {
return read_mode2_sector_win32ioctl( p_env, data, lsn, b_form2 );
}
}
/*!
Reads nblocks of mode2 sectors from cd device into data starting
from lsn.
Returns 0 if no error.
*/
static int
_cdio_read_mode2_sectors (void *user_data, void *data, lsn_t lsn,
bool b_form2, unsigned int nblocks)
{
int i;
int retval;
unsigned int blocksize = b_form2 ? M2RAW_SECTOR_SIZE : CDIO_CD_FRAMESIZE;
for (i = 0; i < nblocks; i++) {
if ( (retval = _cdio_read_mode2_sector (user_data,
((char *)data) + (blocksize * i),
lsn + i, b_form2)) )
return retval;
}
return 0;
}
/*!
Return the size of the CD in logical block address (LBA) units.
*/
static uint32_t
stat_size_win32 (void *user_data)
{
_img_private_t *p_env = user_data;
return p_env->tocent[p_env->gen.i_tracks].start_lsn;
}
/*!
Set the key "arg" to "value" in source device.
*/
static int
set_arg_win32 (void *user_data, const char key[], const char value[])
{
_img_private_t *p_env = user_data;
if (!strcmp (key, "source"))
{
if (!value)
return -2;
free (p_env->gen.source_name);
p_env->gen.source_name = strdup (value);
}
else if (!strcmp (key, "access-mode"))
{
p_env->access_mode = str_to_access_mode_win32(value);
if (p_env->access_mode == _AM_ASPI && !p_env->b_aspi_init)
return init_aspi(p_env) ? 1 : -3;
else if (p_env->access_mode == _AM_IOCTL && !p_env->b_ioctl_init)
return init_win32ioctl(p_env) ? 1 : -3;
else
return -4;
return 0;
}
else
return -1;
return 0;
}
/*!
Read and cache the CD's Track Table of Contents and track info.
Return true if successful or false if an error.
*/
static bool
read_toc_win32 (void *p_user_data)
{
_img_private_t *p_env = p_user_data;
bool ret;
if( p_env->hASPI ) {
ret = read_toc_aspi( p_env );
} else {
ret = read_toc_win32ioctl( p_env );
}
if (ret) p_env->gen.toc_init = true ;
return true;
}
/*!
Eject media. Return 1 if successful, 0 otherwise.
*/
static int
_cdio_eject_media (void *user_data) {
#ifdef _XBOX
return -1;
#else
_img_private_t *env = user_data;
MCI_OPEN_PARMS op;
MCI_STATUS_PARMS st;
DWORD i_flags;
char psz_drive[4];
int ret;
memset( &op, 0, sizeof(MCI_OPEN_PARMS) );
op.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
strcpy( psz_drive, "X:" );
psz_drive[0] = env->gen.source_name[0];
op.lpstrElementName = psz_drive;
/* Set the flags for the device type */
i_flags = MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID |
MCI_OPEN_ELEMENT | MCI_OPEN_SHAREABLE;
if( _cdio_mciSendCommand( 0, MCI_OPEN, i_flags, &op ) ) {
st.dwItem = MCI_STATUS_READY;
/* Eject disc */
ret = _cdio_mciSendCommand( op.wDeviceID, MCI_SET, MCI_SET_DOOR_OPEN, 0 ) != 0;
/* Release access to the device */
_cdio_mciSendCommand( op.wDeviceID, MCI_CLOSE, MCI_WAIT, 0 );
} else
ret = 0;
return ret;
#endif
}
/*!
Return the value associated with the key "arg".
*/
static const char *
_get_arg_win32 (void *user_data, const char key[])
{
_img_private_t *env = user_data;
if (!strcmp (key, "source")) {
return env->gen.source_name;
} else if (!strcmp (key, "access-mode")) {
if (env->hASPI)
return "ASPI";
else
return "ioctl";
}
return NULL;
}
/*!
Return the media catalog number MCN.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
static char *
_cdio_get_mcn (const void *p_user_data) {
const _img_private_t *p_env = p_user_data;
if( p_env->hASPI ) {
return scsi_mmc_get_mcn( p_env->gen.cdio );
} else {
return get_mcn_win32ioctl(p_env);
}
}
/*!
Get format of track.
*/
static track_format_t
_cdio_get_track_format(void *p_obj, track_t i_track)
{
_img_private_t *p_env = p_obj;
if ( !p_env ) return TRACK_FORMAT_ERROR;
if (!p_env->gen.toc_init) read_toc_win32 (p_env) ;
if ( i_track < p_env->gen.i_first_track
|| i_track >= p_env->gen.i_tracks + p_env->gen.i_first_track )
return TRACK_FORMAT_ERROR;
if( p_env->hASPI ) {
return get_track_format_aspi(p_env, i_track);
} else {
return get_track_format_win32ioctl(p_env, i_track);
}
}
/*!
Return true if we have XA data (green, mode2 form1) or
XA data (green, mode2 form2). That is track begins:
sync - header - subheader
12 4 - 8
FIXME: there's gotta be a better design for this and get_track_format?
*/
static bool
_cdio_get_track_green(void *obj, track_t i_track)
{
_img_private_t *p_env = obj;
switch (_cdio_get_track_format(p_env, i_track)) {
case TRACK_FORMAT_XA:
return true;
case TRACK_FORMAT_ERROR:
case TRACK_FORMAT_CDI:
case TRACK_FORMAT_AUDIO:
return false;
case TRACK_FORMAT_DATA:
if (_AM_ASPI == p_env->access_mode )
return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 8) != 0);
default:
break;
}
/* FIXME: Dunno if this is the right way, but it's what
I was using in cd-info for a while.
*/
return ((p_env->tocent[i_track-p_env->gen.i_first_track].Control & 2) != 0);
}
/*!
Return the starting MSF (minutes/secs/frames) for track number
i_tracks in obj. Track numbers start at 1.
The "leadout" track is specified either by
using i_tracks LEADOUT_TRACK or the total tracks+1.
False is returned if there is no track entry.
*/
static bool
_cdio_get_track_msf(void *p_user_data, track_t i_tracks, msf_t *msf)
{
_img_private_t *p_env = p_user_data;
if (NULL == msf) return false;
if (!p_env->gen.toc_init) read_toc_win32 (p_env) ;
if (i_tracks == CDIO_CDROM_LEADOUT_TRACK) i_tracks = p_env->gen.i_tracks+1;
if (i_tracks > p_env->gen.i_tracks+1 || i_tracks == 0) {
return false;
} else {
cdio_lsn_to_msf(p_env->tocent[i_tracks-1].start_lsn, msf);
return true;
}
}
#endif /* HAVE_WIN32_CDROM */
/*!
Return an array of strings giving possible CD devices.
*/
char **
cdio_get_devices_win32 (void)
{
#ifndef HAVE_WIN32_CDROM
return NULL;
#else
char **drives = NULL;
unsigned int num_drives=0;
char drive_letter;
/* Scan the system for CD-ROM drives.
*/
#if FINISHED
/* Now check the currently mounted CD drives */
if (NULL != (ret_drive = cdio_check_mounts("/etc/mtab"))) {
cdio_add_device_list(&drives, drive, &num_drives);
}
/* Finally check possible mountable drives in /etc/fstab */
if (NULL != (ret_drive = cdio_check_mounts("/etc/fstab"))) {
cdio_add_device_list(&drives, drive, &num_drives);
}
#endif
/* Scan the system for CD-ROM drives.
Not always 100% reliable, so use the USE_MNTENT code above first.
*/
for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) {
const char *drive_str=is_cdrom_win32(drive_letter);
if (drive_str != NULL) {
cdio_add_device_list(&drives, drive_str, &num_drives);
}
}
cdio_add_device_list(&drives, NULL, &num_drives);
return drives;
#endif /*HAVE_WIN32_CDROM*/
}
/*!
Return a string containing the default CD device if none is specified.
if CdIo is NULL (we haven't initialized a specific device driver),
then find a suitable one and return the default device for that.
NULL is returned if we couldn't get a default device.
*/
char *
cdio_get_default_device_win32(void)
{
#ifdef HAVE_WIN32_CDROM
char drive_letter;
for (drive_letter='A'; drive_letter <= 'Z'; drive_letter++) {
const char *drive_str=is_cdrom_win32(drive_letter);
if (drive_str != NULL) {
return strdup(drive_str);
}
}
#endif
return NULL;
}
/*!
Return true if source_name could be a device containing a CD-ROM.
*/
bool
cdio_is_device_win32(const char *source_name)
{
unsigned int len;
len = strlen(source_name);
if (NULL == source_name) return false;
#ifdef HAVE_WIN32_CDROM
if ((len == 2) && isalpha(source_name[0])
&& (source_name[len-1] == ':'))
return true;
if ( ! WIN_NT ) return false;
/* Test to see if of form: \\.\x: */
return ( (len == 6)
&& source_name[0] == '\\' && source_name[1] == '\\'
&& source_name[2] == '.' && source_name[3] == '\\'
&& isalpha(source_name[len-2])
&& (source_name[len-1] == ':') );
#else
return false;
#endif
}
/*!
Initialization routine. This is the only thing that doesn't
get called via a function pointer. In fact *we* are the
ones to set that up.
*/
CdIo *
cdio_open_win32 (const char *psz_source_name)
{
#ifdef HAVE_WIN32_CDROM
if ( WIN_NT ) {
return cdio_open_am_win32(psz_source_name, "ioctl");
} else {
return cdio_open_am_win32(psz_source_name, "ASPI");
}
#else
return NULL;
#endif /* HAVE_WIN32_CDROM */
}
/*!
Initialization routine. This is the only thing that doesn't
get called via a function pointer. In fact *we* are the
ones to set that up.
*/
CdIo *
cdio_open_am_win32 (const char *psz_orig_source, const char *psz_access_mode)
{
#ifdef HAVE_WIN32_CDROM
CdIo *ret;
_img_private_t *_data;
char *psz_source;
cdio_funcs _funcs;
memset( &_funcs, 0, sizeof(_funcs) );
_funcs.eject_media = _cdio_eject_media;
_funcs.free = _free_win32;
_funcs.get_arg = _get_arg_win32;
_funcs.get_cdtext = get_cdtext_generic;
_funcs.get_default_device = cdio_get_default_device_win32;
_funcs.get_devices = cdio_get_devices_win32;
_funcs.get_discmode = get_discmode_win32;
_funcs.get_drive_cap = scsi_mmc_get_drive_cap_generic;
_funcs.get_first_track_num= get_first_track_num_generic;
_funcs.get_hwinfo = NULL;
_funcs.get_mcn = _cdio_get_mcn;
_funcs.get_num_tracks = get_num_tracks_generic;
_funcs.get_track_format = _cdio_get_track_format;
_funcs.get_track_green = _cdio_get_track_green;
_funcs.get_track_lba = NULL; /* This could be implemented if need be. */
_funcs.get_track_msf = _cdio_get_track_msf;
_funcs.lseek = NULL;
_funcs.read = NULL;
_funcs.read_audio_sectors = _cdio_read_audio_sectors;
_funcs.read_mode1_sector = _cdio_read_mode1_sector;
_funcs.read_mode1_sectors = _cdio_read_mode1_sectors;
_funcs.read_mode2_sector = _cdio_read_mode2_sector;
_funcs.read_mode2_sectors = _cdio_read_mode2_sectors;
_funcs.read_toc = &read_toc_win32;
_funcs.run_scsi_mmc_cmd = &run_scsi_cmd_win32;
_funcs.set_arg = set_arg_win32;
_funcs.stat_size = stat_size_win32;
_data = _cdio_malloc (sizeof (_img_private_t));
_data->access_mode = str_to_access_mode_win32(psz_access_mode);
_data->gen.init = false;
_data->gen.fd = -1;
if (NULL == psz_orig_source) {
psz_source=cdio_get_default_device_win32();
if (NULL == psz_source) return NULL;
set_arg_win32(_data, "source", psz_source);
free(psz_source);
} else {
if (cdio_is_device_win32(psz_orig_source))
set_arg_win32(_data, "source", psz_orig_source);
else {
/* The below would be okay if all device drivers worked this way. */
#if 0
cdio_info ("source %s is a not a device", psz_orig_source);
#endif
return NULL;
}
}
ret = cdio_new ((void *)_data, &_funcs);
if (ret == NULL) return NULL;
if (_cdio_init_win32(_data))
return ret;
else {
_free_win32 (_data);
return NULL;
}
#else
return NULL;
#endif /* HAVE_WIN32_CDROM */
}
bool
cdio_have_win32 (void)
{
#ifdef HAVE_WIN32_CDROM
return true;
#else
return false;
#endif /* HAVE_WIN32_CDROM */
}

View File

@@ -0,0 +1,170 @@
/*
$Id: win32.h,v 1.1 2004/12/18 17:29:32 rocky Exp $
Copyright (C) 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "cdio_private.h"
#pragma pack()
typedef struct {
lsn_t start_lsn;
UCHAR Control : 4;
UCHAR Format;
cdtext_t cdtext; /* CD-TEXT */
} track_info_t;
typedef enum {
_AM_NONE,
_AM_IOCTL,
_AM_ASPI,
} access_mode_t;
typedef struct {
/* Things common to all drivers like this.
This must be first. */
generic_img_private_t gen;
access_mode_t access_mode;
/* Some of the more OS specific things. */
/* Entry info for each track, add 1 for leadout. */
track_info_t tocent[CDIO_CD_MAX_TRACKS+1];
HANDLE h_device_handle; /* device descriptor */
long hASPI;
short i_sid;
short i_lun;
long (*lpSendCommand)( void* );
bool b_ioctl_init;
bool b_aspi_init;
} _img_private_t;
/*!
Get disc type associated with cd object.
*/
discmode_t get_discmode_win32ioctl (_img_private_t *p_env);
/*!
Reads an audio device using the DeviceIoControl method into data
starting from lsn. Returns 0 if no error.
*/
int read_audio_sectors_win32ioctl (_img_private_t *obj, void *data, lsn_t lsn,
unsigned int nblocks);
/*!
Reads a single mode2 sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
int read_mode2_sector_win32ioctl (const _img_private_t *env, void *data,
lsn_t lsn, bool b_form2);
/*!
Reads a single mode1 sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
int read_mode1_sector_win32ioctl (const _img_private_t *env, void *data,
lsn_t lsn, bool b_form2);
const char *is_cdrom_win32ioctl (const char drive_letter);
/*!
Run a SCSI MMC command.
env private CD structure
i_timeout_ms time in milliseconds we will wait for the command
to complete. If this value is -1, use the default
time-out value.
p_buf Buffer for data, both sending and receiving
i_buf Size of buffer
e_direction direction the transfer is to go.
cdb CDB bytes. All values that are needed should be set on
input. We'll figure out what the right CDB length should be.
Return 0 if command completed successfully.
*/
int run_scsi_cmd_win32ioctl( const void *p_user_data,
unsigned int i_timeout,
unsigned int i_cdb,
const scsi_mmc_cdb_t * p_cdb,
scsi_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf );
/*!
Initialize internal structures for CD device.
*/
bool init_win32ioctl (_img_private_t *env);
/*!
Read and cache the CD's Track Table of Contents and track info.
Return true if successful or false if an error.
*/
bool read_toc_win32ioctl (_img_private_t *env);
/*!
Return the media catalog number MCN.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
char *get_mcn_win32ioctl (const _img_private_t *env);
/*
Read cdtext information for a CdIo object .
return true on success, false on error or CD-TEXT information does
not exist.
*/
bool init_cdtext_win32ioctl (_img_private_t *env);
/*!
Return the the kind of drive capabilities of device.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
void get_drive_cap_aspi (const _img_private_t *env,
cdio_drive_read_cap_t *p_read_cap,
cdio_drive_write_cap_t *p_write_cap,
cdio_drive_misc_cap_t *p_misc_cap);
/*!
Return the the kind of drive capabilities of device.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
void get_drive_cap_win32ioctl (const _img_private_t *env,
cdio_drive_read_cap_t *p_read_cap,
cdio_drive_write_cap_t *p_write_cap,
cdio_drive_misc_cap_t *p_misc_cap);
/*!
Get the format (XA, DATA, AUDIO) of a track.
*/
track_format_t get_track_format_win32ioctl(const _img_private_t *env,
track_t i_track);
void set_cdtext_field_win32(void *user_data, track_t i_track,
track_t i_first_track,
cdtext_field_t e_field, const char *psz_value);

View File

@@ -0,0 +1,739 @@
/*
$Id: win32_ioctl.c,v 1.1 2004/12/18 17:29:32 rocky Exp $
Copyright (C) 2004 Rocky Bernstein <rocky@panix.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* This file contains Win32-specific code using the DeviceIoControl
access method.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static const char _rcsid[] = "$Id: win32_ioctl.c,v 1.1 2004/12/18 17:29:32 rocky Exp $";
#ifdef HAVE_WIN32_CDROM
#if defined (_XBOX)
#include "inttypes.h"
#include "NtScsi.h"
#include "undocumented.h"
#define FORMAT_ERROR(i_err, psz_msg) \
psz_msg=(char *)LocalAlloc(LMEM_ZEROINIT, 255); \
sprintf(psz_msg, "error file %s: line %d (%s) %d\n",
_FILE__, __LINE__, __PRETTY_FUNCTION__, i_err)
#else
#include <ddk/ntddstor.h>
#include <ddk/ntddscsi.h>
#include <ddk/scsi.h>
#define FORMAT_ERROR(i_err, psz_msg) \
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, \
NULL, i_err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), \
(LPSTR) psz_msg, 0, NULL)
#endif
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <stddef.h> /* offsetof() macro */
#include <sys/stat.h>
#include <errno.h>
#include <sys/types.h>
#include <cdio/cdio.h>
#include <cdio/sector.h>
#include "cdio_assert.h"
#include <cdio/scsi_mmc.h>
#include "cdtext_private.h"
#include "cdio/logging.h"
/* Win32 DeviceIoControl specifics */
/***** FIXME: #include ntddcdrm.h from Wine, but probably need to
modify it a little.
*/
#ifndef IOCTL_CDROM_BASE
# define IOCTL_CDROM_BASE FILE_DEVICE_CD_ROM
#endif
#ifndef IOCTL_CDROM_READ_TOC
#define IOCTL_CDROM_READ_TOC \
CTL_CODE(IOCTL_CDROM_BASE, 0x0000, METHOD_BUFFERED, FILE_READ_ACCESS)
#endif
#ifndef IOCTL_CDROM_RAW_READ
#define IOCTL_CDROM_RAW_READ CTL_CODE(IOCTL_CDROM_BASE, 0x000F, \
METHOD_OUT_DIRECT, FILE_READ_ACCESS)
#endif
#ifndef IOCTL_CDROM_READ_Q_CHANNEL
#define IOCTL_CDROM_READ_Q_CHANNEL \
CTL_CODE(IOCTL_CDROM_BASE, 0x000B, METHOD_BUFFERED, FILE_READ_ACCESS)
#endif
typedef struct {
SCSI_PASS_THROUGH Spt;
ULONG Filler;
UCHAR SenseBuf[32];
UCHAR DataBuf[512];
} SCSI_PASS_THROUGH_WITH_BUFFERS;
typedef struct _TRACK_DATA {
UCHAR Format;
UCHAR Control : 4;
UCHAR Adr : 4;
UCHAR TrackNumber;
UCHAR Reserved1;
UCHAR Address[4];
} TRACK_DATA, *PTRACK_DATA;
typedef struct _CDROM_TOC {
UCHAR Length[2];
UCHAR FirstTrack;
UCHAR LastTrack;
TRACK_DATA TrackData[CDIO_CD_MAX_TRACKS+1];
} CDROM_TOC, *PCDROM_TOC;
typedef struct _TRACK_DATA_FULL {
UCHAR SessionNumber;
UCHAR Control : 4;
UCHAR Adr : 4;
UCHAR TNO;
UCHAR POINT; /* Tracknumber (of session?) or lead-out/in (0xA0, 0xA1, 0xA2) */
UCHAR Min; /* Only valid if disctype is CDDA ? */
UCHAR Sec; /* Only valid if disctype is CDDA ? */
UCHAR Frame; /* Only valid if disctype is CDDA ? */
UCHAR Zero; /* Always zero */
UCHAR PMIN; /* start min, if POINT is a track; if lead-out/in 0xA0: First Track */
UCHAR PSEC;
UCHAR PFRAME;
} TRACK_DATA_FULL, *PTRACK_DATA_FULL;
typedef struct _CDROM_TOC_FULL {
UCHAR Length[2];
UCHAR FirstSession;
UCHAR LastSession;
TRACK_DATA_FULL TrackData[CDIO_CD_MAX_TRACKS+3];
} CDROM_TOC_FULL, *PCDROM_TOC_FULL;
typedef enum _TRACK_MODE_TYPE {
YellowMode2,
XAForm2,
CDDA
} TRACK_MODE_TYPE, *PTRACK_MODE_TYPE;
typedef struct __RAW_READ_INFO {
LARGE_INTEGER DiskOffset;
ULONG SectorCount;
TRACK_MODE_TYPE TrackMode;
} RAW_READ_INFO, *PRAW_READ_INFO;
typedef struct _CDROM_SUB_Q_DATA_FORMAT {
UCHAR Format;
UCHAR Track;
} CDROM_SUB_Q_DATA_FORMAT, *PCDROM_SUB_Q_DATA_FORMAT;
typedef struct _SUB_Q_HEADER {
UCHAR Reserved;
UCHAR AudioStatus;
UCHAR DataLength[2];
} SUB_Q_HEADER, *PSUB_Q_HEADER;
typedef struct _SUB_Q_MEDIA_CATALOG_NUMBER {
SUB_Q_HEADER Header;
UCHAR FormatCode;
UCHAR Reserved[3];
UCHAR Reserved1 : 7;
UCHAR Mcval :1;
UCHAR MediaCatalog[15];
} SUB_Q_MEDIA_CATALOG_NUMBER, *PSUB_Q_MEDIA_CATALOG_NUMBER;
#include "win32.h"
#define OP_TIMEOUT_MS 60
/*!
Run a SCSI MMC command.
env private CD structure
i_timeout time in milliseconds we will wait for the command
to complete. If this value is -1, use the default
time-out value.
p_buf Buffer for data, both sending and receiving
i_buf Size of buffer
e_direction direction the transfer is to go.
cdb CDB bytes. All values that are needed should be set on
input. We'll figure out what the right CDB length should be.
Return 0 if command completed successfully.
*/
int
run_scsi_cmd_win32ioctl( const void *p_user_data,
unsigned int i_timeout_ms,
unsigned int i_cdb, const scsi_mmc_cdb_t * p_cdb,
scsi_mmc_direction_t e_direction,
unsigned int i_buf, /*in/out*/ void *p_buf )
{
const _img_private_t *p_env = p_user_data;
SCSI_PASS_THROUGH_DIRECT sptd;
bool success;
DWORD dwBytesReturned;
sptd.Length = sizeof(sptd);
sptd.PathId = 0; /* SCSI card ID will be filled in automatically */
sptd.TargetId= 0; /* SCSI target ID will also be filled in */
sptd.Lun=0; /* SCSI lun ID will also be filled in */
sptd.CdbLength = i_cdb;
sptd.SenseInfoLength = 0; /* Don't return any sense data */
sptd.DataIn = SCSI_MMC_DATA_READ == e_direction ?
SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT;
sptd.DataTransferLength= i_buf;
sptd.TimeOutValue = msecs2secs(i_timeout_ms);
sptd.DataBuffer = (void *) p_buf;
sptd.SenseInfoOffset = 0;
memcpy(sptd.Cdb, p_cdb, i_cdb);
/* Send the command to drive */
success=DeviceIoControl(p_env->h_device_handle,
IOCTL_SCSI_PASS_THROUGH_DIRECT,
(void *)&sptd,
(DWORD)sizeof(SCSI_PASS_THROUGH_DIRECT),
NULL, 0,
&dwBytesReturned,
NULL);
if(! success) {
char *psz_msg = NULL;
long int i_err = GetLastError();
FORMAT_ERROR(i_err, psz_msg);
cdio_info("Error: %s", psz_msg);
LocalFree(psz_msg);
return 1;
}
return 0;
}
/*!
Get disc type associated with cd object.
*/
discmode_t
get_discmode_win32ioctl (_img_private_t *p_env)
{
track_t i_track;
discmode_t discmode=CDIO_DISC_MODE_NO_INFO;
/* See if this is a DVD. */
cdio_dvd_struct_t dvd; /* DVD READ STRUCT for layer 0. */
dvd.physical.type = CDIO_DVD_STRUCT_PHYSICAL;
dvd.physical.layer_num = 0;
if (0 == scsi_mmc_get_dvd_struct_physical_private (p_env,
&run_scsi_cmd_win32ioctl,
&dvd)) {
switch(dvd.physical.layer[0].book_type) {
case CDIO_DVD_BOOK_DVD_ROM: return CDIO_DISC_MODE_DVD_ROM;
case CDIO_DVD_BOOK_DVD_RAM: return CDIO_DISC_MODE_DVD_RAM;
case CDIO_DVD_BOOK_DVD_R: return CDIO_DISC_MODE_DVD_R;
case CDIO_DVD_BOOK_DVD_RW: return CDIO_DISC_MODE_DVD_RW;
case CDIO_DVD_BOOK_DVD_PR: return CDIO_DISC_MODE_DVD_PR;
case CDIO_DVD_BOOK_DVD_PRW: return CDIO_DISC_MODE_DVD_PRW;
default: return CDIO_DISC_MODE_DVD_OTHER;
}
}
if (!p_env->gen.toc_init)
read_toc_win32ioctl (p_env);
if (!p_env->gen.toc_init)
return CDIO_DISC_MODE_NO_INFO;
for (i_track = p_env->gen.i_first_track;
i_track < p_env->gen.i_first_track + p_env->gen.i_tracks ;
i_track ++) {
track_format_t track_fmt=get_track_format_win32ioctl(p_env, i_track);
switch(track_fmt) {
case TRACK_FORMAT_AUDIO:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_DA;
break;
case CDIO_DISC_MODE_CD_DA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_XA:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_XA;
break;
case CDIO_DISC_MODE_CD_XA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_DATA:
switch(discmode) {
case CDIO_DISC_MODE_NO_INFO:
discmode = CDIO_DISC_MODE_CD_DATA;
break;
case CDIO_DISC_MODE_CD_DATA:
case CDIO_DISC_MODE_CD_MIXED:
case CDIO_DISC_MODE_ERROR:
/* No change*/
break;
default:
discmode = CDIO_DISC_MODE_CD_MIXED;
}
break;
case TRACK_FORMAT_ERROR:
default:
discmode = CDIO_DISC_MODE_ERROR;
}
}
return discmode;
}
/*
Returns a string that can be used in a CreateFile call if
c_drive letter is a character. If not NULL is returned.
*/
const char *
is_cdrom_win32ioctl(const char c_drive_letter)
{
#ifdef _XBOX
char sz_win32_drive_full[] = "\\\\.\\X:";
sz_win32_drive_full[4] = c_drive_letter;
return strdup(sz_win32_drive_full);
#else
UINT uDriveType;
char sz_win32_drive[4];
sz_win32_drive[0]= c_drive_letter;
sz_win32_drive[1]=':';
sz_win32_drive[2]='\\';
sz_win32_drive[3]='\0';
uDriveType = GetDriveType(sz_win32_drive);
switch(uDriveType) {
case DRIVE_CDROM: {
char sz_win32_drive_full[] = "\\\\.\\X:";
sz_win32_drive_full[4] = c_drive_letter;
return strdup(sz_win32_drive_full);
}
default:
cdio_debug("Drive %c is not a CD-ROM", c_drive_letter);
return NULL;
}
#endif
}
/*!
Reads an audio device using the DeviceIoControl method into data
starting from lsn. Returns 0 if no error.
*/
int
read_audio_sectors_win32ioctl (_img_private_t *env, void *data, lsn_t lsn,
unsigned int nblocks)
{
DWORD dwBytesReturned;
RAW_READ_INFO cdrom_raw;
/* Initialize CDROM_RAW_READ structure */
cdrom_raw.DiskOffset.QuadPart = CDIO_CD_FRAMESIZE_RAW * lsn;
cdrom_raw.SectorCount = nblocks;
cdrom_raw.TrackMode = CDDA;
if( DeviceIoControl( env->h_device_handle,
IOCTL_CDROM_RAW_READ, &cdrom_raw,
sizeof(RAW_READ_INFO), data,
CDIO_CD_FRAMESIZE_RAW * nblocks,
&dwBytesReturned, NULL ) == 0 ) {
char *psz_msg = NULL;
long int i_err = GetLastError();
FORMAT_ERROR(i_err, psz_msg);
cdio_info("Error reading audio-mode %lu\n%s)",
(long unsigned int) lsn, psz_msg);
LocalFree(psz_msg);
return 1;
}
return 0;
}
/*!
Reads a single raw sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
static int
read_raw_sector (const _img_private_t *p_env, void *p_buf, lsn_t lsn)
{
scsi_mmc_cdb_t cdb = {{0, }};
/* ReadCD CDB12 command. The values were taken from MMC1 draft paper. */
CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_CD);
CDIO_MMC_SET_READ_LBA (cdb.field, lsn);
CDIO_MMC_SET_READ_LENGTH24(cdb.field, 1);
cdb.field[9]=0xF8; /* Raw read, 2352 bytes per sector */
return run_scsi_cmd_win32ioctl(p_env, OP_TIMEOUT_MS,
scsi_mmc_get_cmd_len(cdb.field[0]),
&cdb, SCSI_MMC_DATA_READ,
CDIO_CD_FRAMESIZE_RAW, p_buf);
}
/*!
Reads a single mode2 sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
int
read_mode2_sector_win32ioctl (const _img_private_t *p_env, void *p_data,
lsn_t lsn, bool b_form2)
{
char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
int ret = read_raw_sector (p_env, buf, lsn);
if ( 0 != ret) return ret;
memcpy (p_data,
buf + CDIO_CD_SYNC_SIZE + CDIO_CD_XA_HEADER,
b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
return 0;
}
/*!
Reads a single mode2 sector using the DeviceIoControl method into
data starting from lsn. Returns 0 if no error.
*/
int
read_mode1_sector_win32ioctl (const _img_private_t *env, void *data,
lsn_t lsn, bool b_form2)
{
char buf[CDIO_CD_FRAMESIZE_RAW] = { 0, };
int ret = read_raw_sector (env, buf, lsn);
if ( 0 != ret) return ret;
memcpy (data,
buf + CDIO_CD_SYNC_SIZE+CDIO_CD_HEADER_SIZE,
b_form2 ? M2RAW_SECTOR_SIZE: CDIO_CD_FRAMESIZE);
return 0;
}
/*!
Initialize internal structures for CD device.
*/
bool
init_win32ioctl (_img_private_t *env)
{
#ifdef WIN32
OSVERSIONINFO ov;
#endif
#ifdef _XBOX
ANSI_STRING filename;
OBJECT_ATTRIBUTES attributes;
IO_STATUS_BLOCK status;
HANDLE hDevice;
NTSTATUS error;
#else
unsigned int len=strlen(env->gen.source_name);
char psz_win32_drive[7];
DWORD dw_access_flags;
#endif
cdio_debug("using winNT/2K/XP ioctl layer");
#ifdef WIN32
memset(&ov,0,sizeof(OSVERSIONINFO));
ov.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
GetVersionEx(&ov);
if((ov.dwPlatformId==VER_PLATFORM_WIN32_NT) &&
(ov.dwMajorVersion>4))
dw_access_flags = GENERIC_READ|GENERIC_WRITE; /* add gen write on W2k/XP */
else dw_access_flags = GENERIC_READ;
#endif
if (cdio_is_device_win32(env->gen.source_name))
{
#ifdef _XBOX
// Use XBOX cdrom, no matter what drive letter is given.
RtlInitAnsiString(&filename,"\\Device\\Cdrom0");
InitializeObjectAttributes(&attributes, &filename, OBJ_CASE_INSENSITIVE,
NULL);
error = NtCreateFile( &hDevice,
GENERIC_READ |SYNCHRONIZE | FILE_READ_ATTRIBUTES,
&attributes,
&status,
NULL,
0,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT );
if (!NT_SUCCESS(error))
{
return false;
}
env->h_device_handle = hDevice;
#else
sprintf( psz_win32_drive, "\\\\.\\%c:", env->gen.source_name[len-2] );
env->h_device_handle = CreateFile( psz_win32_drive,
dw_access_flags,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if( env->h_device_handle == INVALID_HANDLE_VALUE )
{
/* No good. try toggle write. */
dw_access_flags ^= GENERIC_WRITE;
env->h_device_handle = CreateFile( psz_win32_drive,
dw_access_flags,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL );
if (env->h_device_handle == NULL)
return false;
}
#endif
env->b_ioctl_init = true;
return true;
}
return false;
}
/*!
Read and cache the CD's Track Table of Contents and track info.
via a SCSI MMC READ_TOC (FULTOC). Return true if successful or
false if an error.
*/
static bool
read_fulltoc_win32mmc (_img_private_t *p_env)
{
scsi_mmc_cdb_t cdb = {{0, }};
CDROM_TOC_FULL cdrom_toc_full;
int i_status, i, i_track_format, i_seen_flag;
/* Operation code */
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
cdb.field[1] = 0x00;
/* Format */
cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC;
memset(&cdrom_toc_full, 0, sizeof(cdrom_toc_full));
/* Setup to read header, to get length of data */
CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(cdrom_toc_full));
i_status = run_scsi_cmd_win32ioctl (p_env, 1000*60*3,
scsi_mmc_get_cmd_len(cdb.field[0]),
&cdb, SCSI_MMC_DATA_READ,
sizeof(cdrom_toc_full), &cdrom_toc_full);
if ( 0 != i_status ) {
cdio_debug ("SCSI MMC READ_TOC failed\n");
return false;
}
i_seen_flag=0;
for( i = 0 ; i <= CDIO_CD_MAX_TRACKS+3; i++ ) {
if ( 0xA0 == cdrom_toc_full.TrackData[i].POINT ) {
/* First track number */
p_env->gen.i_first_track = cdrom_toc_full.TrackData[i].PMIN;
i_track_format = cdrom_toc_full.TrackData[i].PSEC;
i_seen_flag|=0x01;
}
if ( 0xA1 == cdrom_toc_full.TrackData[i].POINT ) {
/* Last track number */
p_env->gen.i_tracks =
cdrom_toc_full.TrackData[i].PMIN - p_env->gen.i_first_track + 1;
i_seen_flag|=0x02;
}
if ( 0xA2 == cdrom_toc_full.TrackData[i].POINT ) {
/* Start position of the lead out */
p_env->tocent[ p_env->gen.i_tracks ].start_lsn =
cdio_msf3_to_lba(
cdrom_toc_full.TrackData[i].PMIN,
cdrom_toc_full.TrackData[i].PSEC,
cdrom_toc_full.TrackData[i].PFRAME );
p_env->tocent[ p_env->gen.i_tracks ].Control
= cdrom_toc_full.TrackData[i].Control;
p_env->tocent[ p_env->gen.i_tracks ].Format = i_track_format;
i_seen_flag|=0x04;
}
if (cdrom_toc_full.TrackData[i].POINT > 0
&& cdrom_toc_full.TrackData[i].POINT <= p_env->gen.i_tracks) {
p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].start_lsn =
cdio_msf3_to_lba(
cdrom_toc_full.TrackData[i].PMIN,
cdrom_toc_full.TrackData[i].PSEC,
cdrom_toc_full.TrackData[i].PFRAME );
p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Control =
cdrom_toc_full.TrackData[i].Control;
p_env->tocent[ cdrom_toc_full.TrackData[i].POINT - 1 ].Format =
i_track_format;
cdio_debug("p_sectors: %i, %lu", i,
(unsigned long int) (p_env->tocent[i].start_lsn));
if (cdrom_toc_full.TrackData[i].POINT == p_env->gen.i_tracks)
i_seen_flag|=0x08;
}
if ( 0x0F == i_seen_flag ) break;
}
if ( 0x0F == i_seen_flag ) {
p_env->gen.toc_init = true;
return true;
}
return false;
}
/*!
Read and cache the CD's Track Table of Contents and track info.
Return true if successful or false if an error.
*/
bool
read_toc_win32ioctl (_img_private_t *p_env)
{
CDROM_TOC cdrom_toc;
DWORD dwBytesReturned;
unsigned int i;
if ( ! p_env ) return false;
if ( read_fulltoc_win32mmc(p_env) ) return true;
/* SCSI-MMC READ_TOC (FULTOC) read failed. Try reading TOC via
DeviceIoControl instead */
if( DeviceIoControl( p_env->h_device_handle,
IOCTL_CDROM_READ_TOC,
NULL, 0, &cdrom_toc, sizeof(CDROM_TOC),
&dwBytesReturned, NULL ) == 0 ) {
char *psz_msg = NULL;
long int i_err = GetLastError();
FORMAT_ERROR(i_err, psz_msg);
if (psz_msg) {
cdio_warn("could not read TOC (%ld): %s", i_err, psz_msg);
LocalFree(psz_msg);
} else
cdio_warn("could not read TOC (%ld)", i_err);
return false;
}
p_env->gen.i_first_track = cdrom_toc.FirstTrack;
p_env->gen.i_tracks = cdrom_toc.LastTrack - cdrom_toc.FirstTrack + 1;
for( i = 0 ; i <= p_env->gen.i_tracks ; i++ ) {
p_env->tocent[ i ].start_lsn =
cdio_msf3_to_lba( cdrom_toc.TrackData[i].Address[1],
cdrom_toc.TrackData[i].Address[2],
cdrom_toc.TrackData[i].Address[3] );
p_env->tocent[ i ].Control = cdrom_toc.TrackData[i].Control;
p_env->tocent[ i ].Format = cdrom_toc.TrackData[i].Format;
cdio_debug("p_sectors: %i, %lu", i,
(unsigned long int) (p_env->tocent[i].start_lsn));
}
p_env->gen.toc_init = true;
return true;
}
/*!
Return the media catalog number MCN.
Note: string is malloc'd so caller should free() then returned
string when done with it.
*/
char *
get_mcn_win32ioctl (const _img_private_t *env) {
DWORD dwBytesReturned;
SUB_Q_MEDIA_CATALOG_NUMBER mcn;
CDROM_SUB_Q_DATA_FORMAT q_data_format;
memset( &mcn, 0, sizeof(mcn) );
q_data_format.Format = CDIO_SUBCHANNEL_MEDIA_CATALOG;
q_data_format.Track=1;
if( DeviceIoControl( env->h_device_handle,
IOCTL_CDROM_READ_Q_CHANNEL,
&q_data_format, sizeof(q_data_format),
&mcn, sizeof(mcn),
&dwBytesReturned, NULL ) == 0 ) {
cdio_warn( "could not read Q Channel at track %d", 1);
} else if (mcn.Mcval)
return strdup(mcn.MediaCatalog);
return NULL;
}
/*!
Get the format (XA, DATA, AUDIO) of a track.
*/
track_format_t
get_track_format_win32ioctl(const _img_private_t *env, track_t i_track)
{
/* This is pretty much copied from the "badly broken" cdrom_count_tracks
in linux/cdrom.c.
*/
if (env->tocent[i_track - env->gen.i_first_track].Control & 0x04) {
if (env->tocent[i_track - env->gen.i_first_track].Format == 0x10)
return TRACK_FORMAT_CDI;
else if (env->tocent[i_track - env->gen.i_first_track].Format == 0x20)
return TRACK_FORMAT_XA;
else
return TRACK_FORMAT_DATA;
} else
return TRACK_FORMAT_AUDIO;
}
#endif /*HAVE_WIN32_CDROM*/