#include "std.h"
#include "emul.h"
#include "vars.h"
#include "init.h"
#include "hddio.h"
#include "util.h"
typedef int (__cdecl *GetASPI32SupportInfo_t)();
typedef int (__cdecl *SendASPI32Command_t)(void *SRB);
const int ATAPI_CDB_SIZE = 12; // sizeof(CDB) == 16
//const int MAX_INFO_LEN = 48;
static GetASPI32SupportInfo_t _GetASPI32SupportInfo = nullptr;
static SendASPI32Command_t _SendASPI32Command = nullptr;
static HMODULE hAspiDll = nullptr;
static HANDLE hASPICompletionEvent;
DWORD ATA_PASSER::open(PHYS_DEVICE *dev)
{
close();
this->dev = dev;
hDevice = CreateFile(dev->filename,
GENERIC_READ | GENERIC_WRITE, // R/W required!
(temp.win9x?0:FILE_SHARE_DELETE) /*Dexus*/ | FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, 0, nullptr);
if (hDevice == INVALID_HANDLE_VALUE)
{
ULONG Le = GetLastError();
printf("can't open: `%s', %lu\n", dev->filename, Le);
return Le;
}
if(dev->type == ATA_NTHDD && dev->usage == ATA_OP_USE)
{
memset(Vols, 0, sizeof(Vols));
// lock & dismount all volumes on disk
char VolName[256];
HANDLE VolEnum = FindFirstVolume(VolName, _countof(VolName));
if(VolEnum == INVALID_HANDLE_VALUE)
{
ULONG Le = GetLastError();
printf("can't enumerate volumes: %lu\n", Le);
return Le;
}
BOOL NextVol = TRUE;
unsigned VolIdx = 0;
unsigned i;
for(; NextVol; NextVol = FindNextVolume(VolEnum, VolName, _countof(VolName)))
{
size_t l = strlen(VolName);
if(VolName[l-1] == '\\')
VolName[l-1] = 0;
HANDLE Vol = CreateFile(VolName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if(Vol == INVALID_HANDLE_VALUE)
{
printf("can't open volume `%s'\n", VolName);
continue;
}
UCHAR Buf[sizeof(VOLUME_DISK_EXTENTS) + 100 * sizeof(DISK_EXTENT)];
PVOLUME_DISK_EXTENTS DiskExt = PVOLUME_DISK_EXTENTS(Buf);
ULONG Junk;
if(!DeviceIoControl(Vol, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, nullptr, 0, Buf, sizeof(Buf), &Junk, nullptr))
{
Junk = GetLastError();
CloseHandle(Vol);
printf("can't get volume extents: `%s', %lu\n", VolName, Junk);
continue;
}
if(DiskExt->NumberOfDiskExtents == 0)
{
// bad volume
CloseHandle(Vol);
return ERROR_ACCESS_DENIED;
}
if(DiskExt->NumberOfDiskExtents > 1)
{
for(i = 0; i < DiskExt->NumberOfDiskExtents; i++)
{
if(DiskExt->Extents[i].DiskNumber == dev->spti_id)
{
// complex volume (volume split over several disks)
CloseHandle(Vol);
return ERROR_ACCESS_DENIED;
}
}
}
if(DiskExt->Extents[0].DiskNumber != dev->spti_id)
{
CloseHandle(Vol);
continue;
}
Vols[VolIdx++] = Vol;
}
FindVolumeClose(VolEnum);
for(i = 0; i < VolIdx; i++)
{
ULONG Junk;
if(!DeviceIoControl(Vols[i], FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &Junk, nullptr))
{
Junk = GetLastError();
printf("can't lock volume: %lu\n", Junk);
return Junk;
}
if(!DeviceIoControl(Vols[i], FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &Junk, nullptr))
{
Junk = GetLastError();
printf("can't dismount volume: %lu\n", Junk);
return Junk;
}
}
}
return NO_ERROR;
}
void ATA_PASSER::close()
{
if (hDevice != INVALID_HANDLE_VALUE)
{
if(dev->type == ATA_NTHDD && dev->usage == ATA_OP_USE)
{
// unlock all volumes on disk
for(unsigned i = 0; i < _countof(Vols) && Vols[i]; i++)
{
ULONG Junk;
DeviceIoControl(Vols[i], FSCTL_UNLOCK_VOLUME, nullptr, 0, nullptr, 0, &Junk, nullptr);
CloseHandle(Vols[i]);
Vols[i] = nullptr;
}
}
CloseHandle(hDevice);
}
hDevice = INVALID_HANDLE_VALUE;
dev = nullptr;
}
unsigned ATA_PASSER::identify(PHYS_DEVICE *outlist, unsigned max)
{
unsigned res = 0;
ATA_PASSER ata;
unsigned HddCount = get_hdd_count();
for (unsigned drive = 0; drive < MAX_PHYS_HD_DRIVES && res < max; drive++)
{
PHYS_DEVICE *dev = outlist + res;
dev->type = ATA_NTHDD;
dev->spti_id = drive;
dev->usage = ATA_OP_ENUM_ONLY;
sprintf(dev->filename, "\\\\.\\PhysicalDrive%u", dev->spti_id);
if(drive >= HddCount)
continue;
DWORD errcode = ata.open(dev);
if (errcode == ERROR_FILE_NOT_FOUND)
continue;
color(CONSCLR_HARDITEM);
printf("hd%u: ", drive);
if (errcode != NO_ERROR)
{
color(CONSCLR_ERROR);
printf("access failed\n");
err_win32(errcode);
continue;
}
SENDCMDINPARAMS in = { 512 };
in.irDriveRegs.bCommandReg = ID_CMD;
struct
{
SENDCMDOUTPARAMS out;
char xx[512];
} res_buffer;
res_buffer.out.cBufferSize = 512;
DWORD sz;
DISK_GEOMETRY geo = { };
int res1 = DeviceIoControl(ata.hDevice, SMART_RCV_DRIVE_DATA, &in, sizeof in, &res_buffer, sizeof res_buffer, &sz, nullptr);
if(!res1)
{
printf("cant get hdd info, %lu\n", GetLastError());
}
int res2 = DeviceIoControl(ata.hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, nullptr, 0, &geo, sizeof geo, &sz, nullptr);
if(!res2)
{
printf("cant get hdd geometry, %lu\n", GetLastError());
}
if (geo.BytesPerSector != 512)
{
color(CONSCLR_ERROR);
printf("unsupported sector size (%lu bytes)\n", geo.BytesPerSector);
continue;
}
ata.close();
if (!res1)
{
color(CONSCLR_ERROR);
printf("identification failed\n");
continue;
}
memcpy(dev->idsector, res_buffer.out.bBuffer, 512);
char model[42], serial[22];
swap_bytes(model, res_buffer.out.bBuffer+54, 20);
swap_bytes(serial, res_buffer.out.bBuffer+20, 10);
dev->hdd_size = geo.Cylinders.LowPart * geo.SectorsPerTrack * geo.TracksPerCylinder;
unsigned shortsize = dev->hdd_size / 2; char mult = 'K';
if (shortsize >= 100000)
{
shortsize /= 1024; mult = 'M';
if(shortsize >= 100000)
{
shortsize /= 1024; mult = 'G';
}
}
color(CONSCLR_HARDINFO);
printf("%-40s %-20s ", model, serial);
color(CONSCLR_HARDITEM);
printf("%8u %cb\n", shortsize, mult);
if (dev->hdd_size > 0xFFFFFFF)
{
color(CONSCLR_WARNING);
printf(" drive %u warning! LBA48 is not supported. only first 128GB visible\n", drive); //Alone Coder 0.36.7
}
print_device_name(dev->viewname, dev);
res++;
}
return res;
}
unsigned ATA_PASSER::get_hdd_count() // static
{
HDEVINFO DeviceInfoSet;
ULONG MemberIndex;
// create a HDEVINFO with all present devices
DeviceInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (DeviceInfoSet == INVALID_HANDLE_VALUE)
{
assert(FALSE);
return 0;
}
// enumerate through all devices in the set
MemberIndex = 0;
while(true)
{
// get device interfaces
SP_DEVICE_INTERFACE_DATA DeviceInterfaceData;
DeviceInterfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
if (!SetupDiEnumDeviceInterfaces(DeviceInfoSet, nullptr, &GUID_DEVINTERFACE_DISK, MemberIndex, &DeviceInterfaceData))
{
if (GetLastError() != ERROR_NO_MORE_ITEMS)
{
// error
assert(FALSE);
}
// ok, reached end of the device enumeration
break;
}
// process the next device next time
MemberIndex++;
}
// destroy device info list
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
return MemberIndex;
}
bool ATA_PASSER::seek(u64 nsector)
{
LARGE_INTEGER offset;
offset.QuadPart = ((__int64)nsector) << 9;
DWORD code = SetFilePointer(hDevice, LONG(offset.LowPart), &offset.HighPart, FILE_BEGIN);
return (code != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
}
bool ATA_PASSER::read_sector(unsigned char *dst)
{
DWORD sz = 0;
if (!ReadFile(hDevice, dst, 512, &sz, nullptr))
return false;
if (sz < 512)
memset(dst+sz, 0, 512-sz); // on EOF, or truncated file, read 00
return true;
}
bool ATA_PASSER::write_sector(unsigned char *src)
{
DWORD sz = 0;
return (WriteFile(hDevice, src, 512, &sz, nullptr) && sz == 512);
}
DWORD ATAPI_PASSER::open(PHYS_DEVICE *dev)
{
close();
this->dev = dev;
if (dev->type == ATA_ASPI_CD)
return NO_ERROR;
hDevice = CreateFile(dev->filename,
GENERIC_READ | GENERIC_WRITE, // R/W required!
(temp.win9x?0:FILE_SHARE_DELETE) /*Dexus*/ | FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_EXISTING, 0, nullptr);
if (hDevice != INVALID_HANDLE_VALUE)
return NO_ERROR;
return GetLastError();
}
void ATAPI_PASSER::close()
{
if (!dev || dev->type == ATA_ASPI_CD)
return;
if (hDevice != INVALID_HANDLE_VALUE)
CloseHandle(hDevice);
hDevice = INVALID_HANDLE_VALUE;
dev = nullptr;
}
unsigned ATAPI_PASSER::identify(PHYS_DEVICE *outlist, unsigned max)
{
unsigned res = 0;
ATAPI_PASSER atapi;
if (conf.cd_aspi)
{
init_aspi();
for (unsigned adapterid = 0; ; adapterid++)
{
SRB_HAInquiry SRB = { };
SRB.SRB_Cmd = SC_HA_INQUIRY;
SRB.SRB_HaId = (unsigned char)adapterid;
int ASPIStatus = _SendASPI32Command(&SRB);
if (ASPIStatus != SS_COMP) break;
char b1[20], b2[20];
memcpy(b1, SRB.HA_ManagerId, 16); b1[16] = 0;
memcpy(b2, SRB.HA_Identifier, 16); b2[16] = 0;
if (adapterid == 0) {
color(CONSCLR_HARDITEM); printf("using ");
color(CONSCLR_WARNING); printf("%s", b1);
color(CONSCLR_HARDITEM); printf(" %s\n", b2);
}
if (adapterid >= SRB.HA_Count) break;
// int maxTargets = (int)SRB.HA_Unique[3]; // always 8 (?)
for (unsigned targetid = 0; targetid < 8; targetid++) {
PHYS_DEVICE *dev = outlist + res;
dev->type = ATA_ASPI_CD;
dev->adapterid = adapterid; // (int)SRB.HA_SCSI_ID; // does not work with Nero ASPI
dev->targetid = targetid;
DWORD errcode = atapi.open(dev);
if (errcode != NO_ERROR) continue;
int ok = atapi.read_atapi_id(dev->idsector, 1);
atapi.close();
if (ok != 2) continue; // not a CD-ROM
print_device_name(dev->viewname, dev);
res++;
}
}
return res;
}
// spti
for (unsigned drive = 0; drive < MAX_PHYS_CD_DRIVES && res < max; drive++)
{
PHYS_DEVICE *dev = outlist + res;
dev->type = ATA_SPTI_CD;
dev->spti_id = drive;
dev->usage = ATA_OP_ENUM_ONLY;
sprintf(dev->filename, "\\\\.\\CdRom%u", dev->spti_id);
DWORD errcode = atapi.open(dev);
if (errcode == ERROR_FILE_NOT_FOUND)
continue;
color(CONSCLR_HARDITEM);
printf("cd%u: ", drive);
if (errcode != NO_ERROR)
{
color(CONSCLR_ERROR);
printf("access failed\n");
err_win32(errcode);
continue;
}
int ok = atapi.read_atapi_id(dev->idsector, 0);
atapi.close();
if (!ok)
{
color(CONSCLR_ERROR);
printf("identification failed\n");
continue;
}
if (ok < 2)
continue; // not a CD-ROM
print_device_name(dev->viewname, dev);
res++;
}
return res;
}
int ATAPI_PASSER::pass_through(void *databuf, size_t bufsize)
{
int res = (conf.cd_aspi)? SEND_ASPI_CMD(databuf, bufsize) : SEND_SPTI_CMD(databuf, bufsize);
return res;
}
int ATAPI_PASSER::SEND_SPTI_CMD(void *databuf, size_t bufsize)
{
memset(databuf, 0, bufsize);
struct {
SCSI_PASS_THROUGH_DIRECT p;
unsigned char sense[MAX_SENSE_LEN];
} srb = { }, dst;
srb.p.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
*(CDB*)&srb.p.Cdb = cdb;
srb.p.CdbLength = sizeof(CDB);
srb.p.DataIn = SCSI_IOCTL_DATA_IN;
srb.p.TimeOutValue = 10;
srb.p.DataBuffer = databuf;
srb.p.DataTransferLength = ULONG(bufsize);
srb.p.SenseInfoLength = sizeof(srb.sense);
srb.p.SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT);
DWORD outsize;
int r = DeviceIoControl(hDevice, IOCTL_SCSI_PASS_THROUGH_DIRECT,
&srb.p, sizeof(srb.p),
&dst, sizeof(dst),
&outsize, nullptr);
if (!r) { return 0; }
passed_length = dst.p.DataTransferLength;
if ((senselen = dst.p.SenseInfoLength)) memcpy(sense, dst.sense, senselen);
#ifdef DUMP_HDD_IO
printf("sense=%d, data=%d, srbsz=%d/%d, dir=%d. ok%d\n", senselen, passed_length, outsize, sizeof(srb.p), dst.p.DataIn, res);
printf("srb:"); dump1((BYTE*)&dst, outsize);
printf("data:"); dump1((BYTE*)databuf, 0x40);
#endif
return 1;
}
int ATAPI_PASSER::read_atapi_id(unsigned char *idsector, char prefix)
{
memset(&cdb, 0, sizeof(CDB));
memset(idsector, 0, 512);
INQUIRYDATA inq;
cdb.CDB6INQUIRY.OperationCode = 0x12; // INQUIRY
cdb.CDB6INQUIRY.AllocationLength = sizeof(inq);
if (!pass_through(&inq, sizeof(inq))) return 0;
char vendor[10], product[18], revision[6], id[22], ata_name[26];
memcpy(vendor, inq.VendorId, sizeof(inq.VendorId)); vendor[sizeof(inq.VendorId)] = 0;
memcpy(product, inq.ProductId, sizeof(inq.ProductId)); product[sizeof(inq.ProductId)] = 0;
memcpy(revision, inq.ProductRevisionLevel, sizeof(inq.ProductRevisionLevel)); revision[sizeof(inq.ProductRevisionLevel)] = 0;
memcpy(id, inq.VendorSpecific, sizeof(inq.VendorSpecific)); id[sizeof(inq.VendorSpecific)] = 0;
if (prefix) {
color(CONSCLR_HARDITEM);
if (dev->type == ATA_ASPI_CD) printf("%u.%u: ", dev->adapterid, dev->targetid);
if (dev->type == ATA_SPTI_CD) printf("cd%u: ", dev->spti_id);
}
trim(vendor); trim(product); trim(revision); trim(id);
sprintf(ata_name, "%s %s", vendor, product);
idsector[0] = 0xC0; // removable, accelerated DRQ, 12-byte packet
idsector[1] = 0x85; // protocol: ATAPI, device type: CD-ROM
make_ata_string(idsector+54, 20, ata_name);
make_ata_string(idsector+20, 10, id);
make_ata_string(idsector+46, 4, revision);
idsector[0x63] = 0x0B; // caps: IORDY,LBA,DMA
idsector[0x67] = 4; // PIO timing
idsector[0x69] = 2; // DMA timing
if (inq.DeviceType == 5) color(CONSCLR_HARDINFO);
printf("%-40s %-20s ", ata_name, id);
color(CONSCLR_HARDITEM);
printf("rev.%-4s\n", revision);
return 1 + (inq.DeviceType == 5);
}
bool ATAPI_PASSER::read_sector(unsigned char *dst)
{
DWORD sz = 0;
if (!ReadFile(hDevice, dst, 2048, &sz, nullptr))
return false;
if (sz < 2048)
memset(dst+sz, 0, 2048-sz); // on EOF, or truncated file, read 00
return true;
}
bool ATAPI_PASSER::seek(unsigned nsector)
{
LARGE_INTEGER offset;
offset.QuadPart = i64(nsector) * 2048;
DWORD code = SetFilePointer(hDevice, LONG(offset.LowPart), &offset.HighPart, FILE_BEGIN);
return (code != INVALID_SET_FILE_POINTER || GetLastError() == NO_ERROR);
}
void make_ata_string(unsigned char *dst, unsigned n_words, const char *src)
{
unsigned i; //Alone Coder 0.36.7
for (/*unsigned*/ i = 0; i < n_words*2 && src[i]; i++) dst[i] = u8(src[i]);
while (i < n_words*2) dst[i++] = ' ';
unsigned char tmp;
for(i = 0; i < n_words * 2; i += 2)
{
tmp = dst[i];
dst[i] = dst[i + 1];
dst[i + 1] = tmp;
}
}
void swap_bytes(char *dst, BYTE *src, unsigned n_words)
{
unsigned i; //Alone Coder 0.36.7
for (/*unsigned*/ i = 0; i < n_words; i++)
{
char c1 = char(src[2*i]), c2 = char(src[2*i+1]);
dst[2 * i] = c2;
dst[2 * i + 1] = c1;
}
dst[2*i] = 0;
trim(dst);
}
void print_device_name(char *dst, PHYS_DEVICE *dev)
{
char model[42], serial[22];
swap_bytes(model, dev->idsector + 54, 20);
swap_bytes(serial, dev->idsector + 20, 10);
sprintf(dst, "<%s,%s>", model, serial);
}
void init_aspi()
{
if (_SendASPI32Command)
return;
hAspiDll = LoadLibrary("WNASPI32.DLL");
if (!hAspiDll)
{
errmsg("failed to load WNASPI32.DLL");
err_win32();
exit();
}
_GetASPI32SupportInfo = (GetASPI32SupportInfo_t)GetProcAddress(hAspiDll, "GetASPI32SupportInfo");
_SendASPI32Command = (SendASPI32Command_t)GetProcAddress(hAspiDll, "SendASPI32Command");
if (!_GetASPI32SupportInfo || !_SendASPI32Command) errexit("invalid ASPI32 library");
int init = _GetASPI32SupportInfo();
if (((init >> 8) & 0xFF) != SS_COMP)
errexit("error in ASPI32 initialization");
hASPICompletionEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
}
int ATAPI_PASSER::SEND_ASPI_CMD(void *buf, size_t buf_sz)
{
SRB_ExecSCSICmd SRB = { };
SRB.SRB_Cmd = SC_EXEC_SCSI_CMD;
SRB.SRB_HaId = (unsigned char)dev->adapterid;
SRB.SRB_Flags = SRB_DIR_IN | SRB_EVENT_NOTIFY | SRB_ENABLE_RESIDUAL_COUNT;
SRB.SRB_Target = (unsigned char)dev->targetid;
SRB.SRB_BufPointer = (unsigned char*)buf;
SRB.SRB_BufLen = DWORD(buf_sz);
SRB.SRB_SenseLen = sizeof(SRB.SenseArea);
SRB.SRB_CDBLen = ATAPI_CDB_SIZE;
SRB.SRB_PostProc = hASPICompletionEvent;
memcpy(SRB.CDBByte, &cdb, ATAPI_CDB_SIZE);
/* DWORD ASPIStatus = */ _SendASPI32Command(&SRB);
passed_length = SRB.SRB_BufLen;
if (SRB.SRB_Status == SS_PENDING)
{
DWORD ASPIEventStatus = WaitForSingleObject(hASPICompletionEvent, 10000); // timeout 10sec
if (ASPIEventStatus == WAIT_OBJECT_0)
ResetEvent(hASPICompletionEvent);
}
senselen = SRB.SRB_SenseLen;
memcpy(sense, SRB.SenseArea, senselen);
if (temp.win9x)
senselen = 0; //Alone Coder //makes possible to read one CD sector in win9x
if (/*(temp.win9x)&&*/(passed_length >= 0xffff))
passed_length = 2048; //Alone Coder //was >=65535 in win9x //makes possible to work in win9x (HDDoct, WDC, Time Gal) //XP fails too
#ifdef DUMP_HDD_IO
printf("sense=%d, data=%d/%d, ok%d\n", senselen, passed_length, buf_sz, SRB.SRB_Status);
printf("srb:"); dump1((BYTE*)&SRB, sizeof(SRB));
printf("data:"); dump1((BYTE*)buf, 0x40);
#endif
return (SRB.SRB_Status == SS_COMP);
}
void done_aspi()
{
if (!hAspiDll)
return;
FreeLibrary(hAspiDll);
CloseHandle(hASPICompletionEvent);
}