Imported Upstream version 0.72.3
This commit is contained in:
441
scsi-win32.c
441
scsi-win32.c
@@ -1,5 +1,5 @@
|
||||
/* dvdisaster: Additional error correction for optical media.
|
||||
* Copyright (C) 2004-2010 Carsten Gnoerlich.
|
||||
* Copyright (C) 2004-2011 Carsten Gnoerlich.
|
||||
* Project home page: http://www.dvdisaster.com
|
||||
* Email: carsten@dvdisaster.com -or- cgnoerlich@fsfe.org
|
||||
*
|
||||
@@ -27,15 +27,23 @@
|
||||
/**
|
||||
** Windows wrapper.
|
||||
**
|
||||
* As of dvdisaster 0.73.1, Windows 2000 SP4 is the minimum
|
||||
* system requirement and thus only SPTI is supported.
|
||||
* Actually we have two wrappers; one for SPTI and one for ASPI.
|
||||
* SPTI requires Windows 2000 or XP and root priviledges, but seems
|
||||
* to be more compatible.
|
||||
* Otoh, ASPI run without special priviledges and even on the older
|
||||
* Windows 9x versions.
|
||||
* The ASPI wrapper has only been tested against the WNASPI32.DLL
|
||||
* made by Adaptec.
|
||||
*
|
||||
* SPTI is tried first and then we fall back to ASPI.
|
||||
*/
|
||||
|
||||
#ifdef SYS_MINGW
|
||||
|
||||
/*
|
||||
* The drive letter ordering is remembered for
|
||||
* historical reasons (it was once useful for ASPI mapping)
|
||||
* This is independent from later decision between SPTI and ASPI.
|
||||
* The drive letter ordering is remembered to aid the
|
||||
* drive letter / ASPI drive guessing / mapping.
|
||||
*/
|
||||
|
||||
static int drive_letters[26];
|
||||
@@ -75,6 +83,20 @@ char* DefaultDevice()
|
||||
}
|
||||
}
|
||||
|
||||
/* Try looking for drives using ASPI.
|
||||
Changed behaviour since V0.72:
|
||||
We provide both SPTI and ASPI in the GUI. */
|
||||
|
||||
if(Closure->aspiLib)
|
||||
{ int none_picked = !Closure->deviceNodes->len;
|
||||
DeviceHandle *dh = open_aspi_device("A:", 2);
|
||||
if(dh) CloseDevice(dh);
|
||||
|
||||
if(none_picked
|
||||
&& Closure->deviceNodes->len) /* pick first aspi drive */
|
||||
*picked = ((char*)g_ptr_array_index(Closure->deviceNodes,0))[0];
|
||||
}
|
||||
|
||||
if(cd_dvd_drives > 0)
|
||||
return g_strdup(picked);
|
||||
|
||||
@@ -82,7 +104,7 @@ char* DefaultDevice()
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the SPTI device.
|
||||
* Close the SPTI/ASPI devices.
|
||||
*/
|
||||
|
||||
void CloseDevice(DeviceHandle *dh)
|
||||
@@ -93,8 +115,9 @@ void CloseDevice(DeviceHandle *dh)
|
||||
if(dh->rawBuffer)
|
||||
FreeRawBuffer(dh->rawBuffer);
|
||||
|
||||
if(dh->fd) /* SPTI cleanup */
|
||||
CloseHandle(dh->fd);
|
||||
if(!dh->aspiUsed) /* SPTI cleanup */
|
||||
{ CloseHandle(dh->fd);
|
||||
}
|
||||
|
||||
if(dh->rs02Header)
|
||||
g_free(dh->rs02Header);
|
||||
@@ -111,6 +134,394 @@ void CloseDevice(DeviceHandle *dh)
|
||||
g_free(dh);
|
||||
}
|
||||
|
||||
/**
|
||||
** The ASPI wrapper.
|
||||
**/
|
||||
|
||||
/*
|
||||
* Not everyone has the development kit with the #includes
|
||||
* for sending SCSI packets via ASPI, so we define what we need here.
|
||||
*/
|
||||
|
||||
#define SS_PENDING 0x00
|
||||
#define SS_COMP 0x01
|
||||
#define SS_ERR 0x04
|
||||
|
||||
#define SRB_DIR_IN 0x08
|
||||
#define SRB_DIR_OUT 0x10
|
||||
#define SRB_EVENT_NOTIFY 0x40
|
||||
|
||||
#define SC_HA_INQUIRY 0x00 /* Host adapter inquiry */
|
||||
#define SC_GET_DEV_TYPE 0x01 /* Get device type */
|
||||
#define SC_EXEC_SCSI_CMD 0x02 /* Execute SCSI command */
|
||||
|
||||
#define DTYPE_DASD 0
|
||||
#define DTYPE_CDROM 5
|
||||
|
||||
typedef struct
|
||||
{ BYTE Cmd; /* ASPI command code = SC_HA_INQUIRY */
|
||||
BYTE Status; /* ASPI command status byte */
|
||||
BYTE HaId; /* ASPI host adapter number */
|
||||
BYTE Flags; /* ASPI request flags */
|
||||
DWORD Hdr_Rsvd; /* Reserved, MUST = 0 */
|
||||
BYTE Count; /* Number of host adapters present */
|
||||
BYTE SCSI_ID; /* SCSI ID of host adapter */
|
||||
BYTE HA_ManagerId[16]; /* String describing the manager */
|
||||
BYTE HA_Identifier[16]; /* String describing the host adapter */
|
||||
WORD HA_BufAlignMask; /* HA_Unique[1-0] */
|
||||
BYTE HA_Flags; /* HA_Unique[2] */
|
||||
BYTE HA_MaxTargets; /* HA_Unique[3] */
|
||||
DWORD HA_MaxTransferLength; /* HA_Unique[7-4] */
|
||||
DWORD HA_MaxSGElements; /* HA_Unique[11-8] */
|
||||
BYTE HA_Rsvd2[4]; /* HA_Unique[15-12] */
|
||||
WORD HA_Rsvd1; /* Reserved, MUST = 0 */
|
||||
} PACKED SRB32_HAInquiry;
|
||||
|
||||
typedef struct
|
||||
{ BYTE Cmd; /* ASPI command code = SC_GET_DEV_TYPE */
|
||||
BYTE Status; /* ASPI command status byte */
|
||||
BYTE HaId; /* ASPI host adapter number */
|
||||
BYTE Flags; /* Reserved, MUST = 0 */
|
||||
DWORD Hdr_Rsvd; /* Reserved, MUST = 0 */
|
||||
BYTE Target; /* Target's SCSI ID */
|
||||
BYTE Lun; /* Target's LUN number */
|
||||
BYTE DeviceType; /* Target's peripheral device type */
|
||||
BYTE Rsvd1; /* Reserved, MUST = 0 */
|
||||
} PACKED SRB32_GDEVBlock;
|
||||
|
||||
typedef struct
|
||||
{ BYTE Cmd; /* ASPI command code = SC_EXEC_SCSI_CMD */
|
||||
BYTE Status; /* ASPI command status byte */
|
||||
BYTE HaId; /* ASPI host adapter number */
|
||||
BYTE Flags; /* ASPI request flags */
|
||||
DWORD Hdr_Rsvd; /* Reserved */
|
||||
BYTE Target; /* Target's SCSI ID */
|
||||
BYTE Lun; /* Target's LUN number */
|
||||
WORD Rsvd1; /* Reserved for Alignment */
|
||||
DWORD BufLen; /* Data Allocation Length */
|
||||
BYTE *BufPtr; /* Data Buffer Pointer */
|
||||
BYTE SenseLen; /* Sense Allocation Length */
|
||||
BYTE CDBLen; /* CDB Length */
|
||||
BYTE HaStat; /* Host Adapter Status */
|
||||
BYTE TargStat; /* Target Status */
|
||||
VOID *PostProc; /* Post routine */
|
||||
BYTE Rsvd2[20]; /* Reserved, MUST be 0 */
|
||||
BYTE CDBByte[16]; /* SCSI CDB */
|
||||
BYTE SenseArea[16]; /* Request Sense buffer */
|
||||
} PACKED SRB32_ExecSCSICmd;
|
||||
|
||||
/*
|
||||
* Open and close the ASPI library.
|
||||
*/
|
||||
|
||||
void OpenAspi()
|
||||
{
|
||||
/* Try to open the ASPI library */
|
||||
|
||||
Closure->aspiLib = LoadLibrary("WNASPI32.DLL");
|
||||
if(!Closure->aspiLib) return;
|
||||
|
||||
Closure->GetASPI32SupportInfo
|
||||
= (DWORD(*)(void))GetProcAddress(Closure->aspiLib, "GetASPI32SupportInfo");
|
||||
if(!Closure->GetASPI32SupportInfo)
|
||||
{ PrintLog("GetASPI32SupportInfo() not available.");
|
||||
FreeLibrary(Closure->aspiLib);
|
||||
Closure->aspiLib = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
Closure->SendASPI32Command
|
||||
= (DWORD(*)(void*))GetProcAddress(Closure->aspiLib, "SendASPI32Command");
|
||||
if(!Closure->SendASPI32Command)
|
||||
{ PrintLog("SendASPI32Command() not available.");
|
||||
FreeLibrary(Closure->aspiLib);
|
||||
Closure->aspiLib = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
/* The Adaptec docs seem to imply that this call is needed
|
||||
to initialize the ASPI library. */
|
||||
|
||||
Closure->GetASPI32SupportInfo();
|
||||
}
|
||||
|
||||
void CloseAspi()
|
||||
{
|
||||
if(Closure->aspiLib)
|
||||
{ FreeLibrary(Closure->aspiLib);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Open and prepare the device using ASPI.
|
||||
*/
|
||||
|
||||
#define LIST_PRINT 1
|
||||
#define LIST_COLLECT 2
|
||||
|
||||
DeviceHandle* open_aspi_device(char *device, int list_mode)
|
||||
{ DeviceHandle *dh;
|
||||
int status,ret,ha,max_ha;
|
||||
SRB32_HAInquiry ha_inq;
|
||||
int drive_count = 0;
|
||||
int drive_wanted = 0;
|
||||
char spti_name[3];
|
||||
char letter_wanted = toupper(*device);
|
||||
|
||||
dh = g_malloc0(sizeof(DeviceHandle));
|
||||
dh->device = g_strdup(device);
|
||||
|
||||
/* Look for our special ASPI drive syntax */
|
||||
|
||||
if(*device >= '1' && *device <= '9')
|
||||
drive_wanted = *device - '0';
|
||||
|
||||
/* Bail out if no ASPI available */
|
||||
|
||||
if(!Closure->aspiLib)
|
||||
{ g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dh->aspiUsed = TRUE;
|
||||
|
||||
/* Get number of host adapters. */
|
||||
|
||||
ret = Closure->GetASPI32SupportInfo();
|
||||
status = (ret>>8) & 0xff;
|
||||
max_ha = ret & 0xff;
|
||||
|
||||
if(status != SS_COMP)
|
||||
{ PrintLog("Could not determine number of host adapters\n");
|
||||
g_free(dh->device);
|
||||
g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
PrintLog("Status %d, %d host adapters\n",status,max_ha);
|
||||
#endif
|
||||
|
||||
/* Now see if we can find any CDROM drives. */
|
||||
|
||||
for(ha=0; ha<max_ha; ha++)
|
||||
{ int target,n_targets;
|
||||
|
||||
memset(&ha_inq, 0, sizeof(SRB32_HAInquiry));
|
||||
ha_inq.Cmd = SC_HA_INQUIRY;
|
||||
ha_inq.HaId = ha;
|
||||
|
||||
Closure->SendASPI32Command(&ha_inq);
|
||||
if(ha_inq.Status != SS_COMP)
|
||||
PrintLog("ASPI warning: Could not query host adapter %d\n",ha);
|
||||
|
||||
#if 0
|
||||
PrintLog("HA %d: %16s\n",ha,ha_inq.HA_Identifier);
|
||||
#endif
|
||||
|
||||
if(ha_inq.HA_MaxTargets == 16) /* my interpretation of */
|
||||
n_targets = 16; /* Adaptecs documentation */
|
||||
else n_targets = 8;
|
||||
|
||||
#if 0
|
||||
/* Some ASPI drivers return -1 or 0 for actually working
|
||||
configurations, so we can`t rely on this information. */
|
||||
|
||||
if(ha_inq.HA_MaxTransferLength == 0)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
/* Missing the following conditions is close to impossible. */
|
||||
|
||||
if(ha_inq.HA_BufAlignMask >= 4096)
|
||||
{ Stop("ASPI alignment = %d requested; can't handle that.\n",
|
||||
ha_inq.HA_BufAlignMask);
|
||||
g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
#if 0
|
||||
/* Some ASPI drivers return 0 or -1 for actually working drives,
|
||||
so this information is also useless. */
|
||||
|
||||
if(ha_inq.HA_MaxTransferLength < MAX_CLUSTER_SIZE)
|
||||
{ Stop("ASPI max xfer length = %d; can't handle that.\n",
|
||||
ha_inq.HA_MaxTransferLength);
|
||||
g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Iterate over the HA's possible targets */
|
||||
|
||||
for(target=0; target<n_targets; target++)
|
||||
{ SRB32_GDEVBlock gdb;
|
||||
|
||||
if(target == ha_inq.SCSI_ID) /* ignore the HA itself */
|
||||
continue;
|
||||
|
||||
memset(&gdb, 0, sizeof(SRB32_GDEVBlock));
|
||||
gdb.Cmd = SC_GET_DEV_TYPE;
|
||||
gdb.HaId = ha;
|
||||
gdb.Target = target;
|
||||
gdb.Lun = 0; /* Are there CDROMs with Lun != 0 ? */
|
||||
|
||||
Closure->SendASPI32Command(&gdb);
|
||||
|
||||
if(gdb.Status != SS_COMP)
|
||||
continue; /* device does not exist */
|
||||
|
||||
if(gdb.DeviceType == DTYPE_CDROM)
|
||||
{ char guessed_letter = drive_letters[drive_count];
|
||||
|
||||
drive_count++;
|
||||
|
||||
dh->ha = ha;
|
||||
dh->target = target;
|
||||
dh->lun = 0;
|
||||
|
||||
if(list_mode)
|
||||
{ InquireDevice(dh, 1);
|
||||
if(drive_count<26 && guessed_letter)
|
||||
{ spti_name[0] = guessed_letter;
|
||||
spti_name[1] = ':';
|
||||
}
|
||||
else spti_name[0] = spti_name[1] = '?';
|
||||
spti_name[2] = 0;
|
||||
|
||||
if(list_mode == LIST_PRINT)
|
||||
PrintLog(" %d: (%s) %s\n", drive_count, spti_name, dh->devinfo);
|
||||
else
|
||||
{ char buf[50]; /* devinfo is 34 */
|
||||
|
||||
sprintf(buf, "%c: %s [ASPI# %d:]",
|
||||
guessed_letter, dh->devinfo, drive_count);
|
||||
g_ptr_array_add(Closure->deviceNames, g_strdup(buf));
|
||||
sprintf(buf, "%d:", drive_count);
|
||||
g_ptr_array_add(Closure->deviceNodes, g_strdup(buf));
|
||||
}
|
||||
}
|
||||
|
||||
if(drive_wanted && drive_wanted == drive_count)
|
||||
return dh; /* drive found by our ASPI syntax */
|
||||
|
||||
if(letter_wanted == guessed_letter)
|
||||
return dh; /* hopefully found the right drive for letter */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(list_mode == LIST_PRINT)
|
||||
{ if(!drive_count)
|
||||
PrintLog(_("ASPI manager present, but no CD/DVD drives managed.\n"));
|
||||
else if(drive_count != cd_dvd_drives)
|
||||
LogWarning(_("%d SPTI drives, but %d ASPI drives.\n"
|
||||
"Drive letter mapping for ASPI drives is probably incorrect.\n"),
|
||||
cd_dvd_drives, drive_count);
|
||||
return dh;
|
||||
}
|
||||
|
||||
g_free(dh->device);
|
||||
g_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print all CDROM drives accessible over ASPI.
|
||||
*/
|
||||
|
||||
void ListAspiDrives()
|
||||
{ DeviceHandle *dh;
|
||||
|
||||
PrintCLI(_("\nList of ASPI CD/DVD drives:\n"));
|
||||
|
||||
dh = open_aspi_device("A:", LIST_PRINT);
|
||||
|
||||
if(dh)
|
||||
{ PrintCLI(_("\nTo force ASPI usage over SPTI, refer to the drive by the\n"
|
||||
"above numbers (use 1:, 2:,... instead of C:, D:,...)\n"));
|
||||
|
||||
CloseDevice(dh);
|
||||
}
|
||||
else PrintCLI(_("ASPI manager not available or installed.\n"));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Send the SCSI command through ASPI.
|
||||
*/
|
||||
|
||||
static int send_aspi_packet(DeviceHandle *dh, unsigned char *cmd, int cdb_size, char *buf, int size, Sense *sense, int data_mode)
|
||||
{ SRB32_ExecSCSICmd srb;
|
||||
DWORD status;
|
||||
HANDLE srb_event;
|
||||
|
||||
srb_event = CreateEvent(NULL, 1, 0, NULL);
|
||||
|
||||
/* Prepare the SRB struct */
|
||||
|
||||
memset(&srb, 0, sizeof(SRB32_ExecSCSICmd));
|
||||
srb.Cmd = SC_EXEC_SCSI_CMD;
|
||||
srb.HaId = dh->ha;
|
||||
srb.Target = dh->target;
|
||||
srb.Lun = dh->lun;
|
||||
switch(data_mode)
|
||||
{ case DATA_WRITE:
|
||||
srb.Flags = SRB_DIR_OUT | SRB_EVENT_NOTIFY;
|
||||
break;
|
||||
case DATA_READ:
|
||||
srb.Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY;
|
||||
break;
|
||||
case DATA_NONE:
|
||||
srb.Flags = SRB_EVENT_NOTIFY;
|
||||
break;
|
||||
default:
|
||||
Stop("illegal data_mode for ASPI: %d", data_mode);
|
||||
return -1;
|
||||
}
|
||||
srb.BufPtr = buf;
|
||||
srb.BufLen = size;
|
||||
srb.SenseLen = 16;
|
||||
srb.PostProc = srb_event;
|
||||
|
||||
srb.CDBLen = cdb_size;
|
||||
memcpy(&srb.CDBByte, cmd, cdb_size);
|
||||
|
||||
/* Send the SCSI command */
|
||||
|
||||
ResetEvent(srb_event);
|
||||
status = Closure->SendASPI32Command(&srb);
|
||||
|
||||
if(status == SS_PENDING)
|
||||
WaitForSingleObject(srb_event, INFINITE);
|
||||
|
||||
CloseHandle(srb_event);
|
||||
memcpy(sense, &srb.SenseArea, 16);
|
||||
|
||||
/* SS_COMP implies no SCSI error */
|
||||
|
||||
if(srb.Status == SS_COMP) return 0;
|
||||
|
||||
/* Now see what went wrong */
|
||||
|
||||
switch(srb.TargStat)
|
||||
{ case 0x00: /* STATUS_GOOD */
|
||||
return -1;
|
||||
|
||||
case 0x02: /* CHECK CONDITION */
|
||||
return -1;
|
||||
|
||||
case 0x08: /* BUSY */
|
||||
PrintLog("ASPI layer: Target busy.\n");
|
||||
return -1;
|
||||
|
||||
case 0x18: /* Reservation conflict */
|
||||
PrintLog("ASPI layer: Reservation conflict.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
** The SPTI wrapper.
|
||||
**/
|
||||
@@ -230,6 +641,10 @@ static int send_spti_packet(HANDLE fd, unsigned char *cmd, int cdb_size, char *b
|
||||
return 0;
|
||||
}
|
||||
|
||||
/***
|
||||
*** ASPI/SPTI wrapping
|
||||
***/
|
||||
|
||||
/*
|
||||
* Open the device
|
||||
*/
|
||||
@@ -241,8 +656,10 @@ DeviceHandle* OpenDevice(char *device)
|
||||
|| (*device >= 'C' && *device <= 'Z'))
|
||||
dh = open_spti_device(Closure->device);
|
||||
|
||||
if(!dh) dh = open_aspi_device(Closure->device, 0);
|
||||
|
||||
if(!dh)
|
||||
{ Stop(_("\nCould not open device %s."), device);
|
||||
{ Stop(_("\nNeither SPTI nor ASPI worked opening %s."), device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -250,12 +667,14 @@ DeviceHandle* OpenDevice(char *device)
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate Scsi wrapper into SPTI call
|
||||
* Dispatch between SPTI and ASPI for packet sending.
|
||||
*/
|
||||
|
||||
int SendPacket(DeviceHandle *dh, unsigned char *cmd, int cdb_size, unsigned char *buf, int size, Sense *sense, int data_mode)
|
||||
{
|
||||
return send_spti_packet(dh->fd, cmd, cdb_size, buf, size, sense, data_mode);
|
||||
if(dh->aspiUsed)
|
||||
return send_aspi_packet(dh, cmd, cdb_size, buf, size, sense, data_mode);
|
||||
else return send_spti_packet(dh->fd, cmd, cdb_size, buf, size, sense, data_mode);
|
||||
}
|
||||
|
||||
#endif /* SYS_MINGW */
|
||||
|
||||
Reference in New Issue
Block a user