788 lines
26 KiB
C
788 lines
26 KiB
C
/*
|
|
* 86Box A hypervisor and IBM PC system emulator that specializes in
|
|
* running old operating systems and software designed for IBM
|
|
* PC systems and compatibles from 1981 through fairly recent
|
|
* system designs based on the PCI bus.
|
|
*
|
|
* This file is part of the 86Box distribution.
|
|
*
|
|
* Win32 CD-ROM support via IOCTL.
|
|
*
|
|
*
|
|
*
|
|
* Authors: TheCollector1995, <mariogplayer@gmail.com>,
|
|
* Miran Grca, <mgrca8@gmail.com>
|
|
*
|
|
* Copyright 2023 TheCollector1995.
|
|
* Copyright 2023 Miran Grca.
|
|
*/
|
|
#define UNICODE
|
|
#define BITMAP WINDOWS_BITMAP
|
|
#include <windows.h>
|
|
#undef BITMAP
|
|
#include <inttypes.h>
|
|
#include "ntddcdrm.h"
|
|
#include "ntddscsi.h"
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
#include <stdarg.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <wchar.h>
|
|
#include <86box/cdrom.h>
|
|
#include <86box/log.h>
|
|
#include <86box/plat_cdrom_ioctl.h>
|
|
|
|
typedef struct ioctl_t {
|
|
cdrom_t *dev;
|
|
void *log;
|
|
int is_dvd;
|
|
int has_audio;
|
|
int32_t tracks_num;
|
|
int toc_valid;
|
|
uint8_t cur_toc[65536];
|
|
CDROM_READ_TOC_EX cur_read_toc_ex;
|
|
int blocks_num;
|
|
uint8_t cur_rti[65536];
|
|
HANDLE handle;
|
|
WCHAR path[256];
|
|
} ioctl_t;
|
|
|
|
static void ioctl_read_toc(ioctl_t *ioctl);
|
|
static int ioctl_read_dvd_structure(const void *local, uint8_t layer, uint8_t format,
|
|
uint8_t *buffer, uint32_t *info);
|
|
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
int ioctl_do_log = ENABLE_IOCTL_LOG;
|
|
|
|
void
|
|
ioctl_log(void *priv, const char *fmt, ...)
|
|
{
|
|
if (ioctl_do_log) {
|
|
va_list ap;
|
|
va_start(ap, fmt);
|
|
log_out(priv, fmt, ap);
|
|
va_end(ap);
|
|
}
|
|
}
|
|
#else
|
|
# define ioctl_log(priv, fmt, ...)
|
|
#endif
|
|
|
|
/* Internal functions. */
|
|
static void
|
|
ioctl_close_handle(const ioctl_t *ioctl)
|
|
{
|
|
if (ioctl->handle != NULL)
|
|
CloseHandle(ioctl->handle);
|
|
}
|
|
|
|
static int
|
|
ioctl_open_handle(ioctl_t *ioctl)
|
|
{
|
|
ioctl_log(ioctl->log, "ioctl->path = \"%ls\"\n", ioctl->path);
|
|
ioctl->handle = CreateFileW((LPCWSTR) ioctl->path, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
|
|
ioctl_log(ioctl->log, "handle=%p, error=%x\n",
|
|
ioctl->handle, (unsigned int) GetLastError());
|
|
|
|
return (ioctl->handle != INVALID_HANDLE_VALUE);
|
|
}
|
|
|
|
static int
|
|
ioctl_load(ioctl_t *ioctl)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (ioctl_open_handle(ioctl)) {
|
|
long size;
|
|
DeviceIoControl(ioctl->handle, IOCTL_STORAGE_LOAD_MEDIA,
|
|
NULL, 0, NULL, 0,
|
|
(LPDWORD) &size, NULL);
|
|
ret = 1;
|
|
ioctl_close_handle(ioctl);
|
|
|
|
ioctl_read_toc(ioctl);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ioctl_read_normal_toc(ioctl_t *ioctl, uint8_t *toc_buf)
|
|
{
|
|
long size = 0;
|
|
PCDROM_TOC_FULL_TOC_DATA cur_full_toc = NULL;
|
|
|
|
ioctl->tracks_num = 0;
|
|
memset(toc_buf, 0x00, 65536);
|
|
|
|
cur_full_toc = (PCDROM_TOC_FULL_TOC_DATA) calloc(1, 65536);
|
|
|
|
ioctl->cur_read_toc_ex.Format = CDROM_READ_TOC_EX_FORMAT_TOC;
|
|
ioctl_log(ioctl->log, "cur_read_toc_ex.Format = %i\n", ioctl->cur_read_toc_ex.Format);
|
|
ioctl->cur_read_toc_ex.Msf = 1;
|
|
ioctl->cur_read_toc_ex.SessionTrack = 1;
|
|
|
|
ioctl_open_handle(ioctl);
|
|
const int temp = DeviceIoControl(ioctl->handle, IOCTL_CDROM_READ_TOC_EX,
|
|
&ioctl->cur_read_toc_ex, 65535,
|
|
cur_full_toc, 65535,
|
|
(LPDWORD) &size, NULL);
|
|
ioctl_close_handle(ioctl);
|
|
ioctl_log(ioctl->log, "temp = %i\n", temp);
|
|
|
|
if (temp != 0) {
|
|
const int length = ((cur_full_toc->Length[0] << 8) | cur_full_toc->Length[1]) + 2;
|
|
memcpy(toc_buf, cur_full_toc, length);
|
|
ioctl->tracks_num = (length - 4) / 8;
|
|
}
|
|
|
|
free(cur_full_toc);
|
|
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
PCDROM_TOC toc = (PCDROM_TOC) toc_buf;
|
|
|
|
ioctl_log(ioctl->log, "%i tracks: %02X %02X %02X %02X\n",
|
|
ioctl->tracks_num, toc_buf[0], toc_buf[1], toc_buf[2], toc_buf[3]);
|
|
|
|
for (int i = 0; i < ioctl->tracks_num; i++) {
|
|
const uint8_t *t = (const uint8_t *) &toc->TrackData[i];
|
|
ioctl_log(ioctl->log, "Track %03i: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
i, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7]);
|
|
}
|
|
#endif
|
|
|
|
return temp;
|
|
}
|
|
|
|
static void
|
|
ioctl_read_raw_toc(ioctl_t *ioctl)
|
|
{
|
|
PCDROM_TOC_FULL_TOC_DATA cur_full_toc = NULL;
|
|
long size = 0;
|
|
raw_track_info_t *rti = (raw_track_info_t *) ioctl->cur_rti;
|
|
uint8_t *buffer = (uint8_t *) calloc (1, 2052);
|
|
|
|
ioctl->is_dvd = (ioctl_read_dvd_structure(ioctl, 0, 0, buffer, NULL) > 0);
|
|
free(buffer);
|
|
|
|
ioctl->has_audio = 0;
|
|
ioctl->blocks_num = 0;
|
|
memset(ioctl->cur_rti, 0x00, 65536);
|
|
|
|
cur_full_toc = (PCDROM_TOC_FULL_TOC_DATA) calloc(1, 65536);
|
|
|
|
ioctl->cur_read_toc_ex.Format = CDROM_READ_TOC_EX_FORMAT_FULL_TOC;
|
|
ioctl_log(ioctl->log, "cur_read_toc_ex.Format = %i\n", ioctl->cur_read_toc_ex.Format);
|
|
ioctl->cur_read_toc_ex.Msf = 1;
|
|
ioctl->cur_read_toc_ex.SessionTrack = 1;
|
|
|
|
ioctl_open_handle(ioctl);
|
|
const int status = DeviceIoControl(ioctl->handle, IOCTL_CDROM_READ_TOC_EX,
|
|
&ioctl->cur_read_toc_ex, 65535,
|
|
cur_full_toc, 65535,
|
|
(LPDWORD) &size, NULL);
|
|
ioctl_close_handle(ioctl);
|
|
ioctl_log(ioctl->log, "status = %i\n", status);
|
|
|
|
if ((status == 0) && (ioctl->tracks_num >= 1)) {
|
|
/*
|
|
This is needed because in some circumstances (eg. a DVD .MDS
|
|
mounted in Daemon Tools), reading the raw TOC fails but
|
|
reading the cooked TOC does not, so we have to construct the
|
|
raw TOC from the cooked TOC.
|
|
*/
|
|
const CDROM_TOC *toc = (const CDROM_TOC *) ioctl->cur_toc;
|
|
const TRACK_DATA *ct = &(toc->TrackData[ioctl->tracks_num - 1]);
|
|
|
|
rti[0].adr_ctl = ((ct->Adr & 0xf) << 4) | (ct->Control & 0xf);
|
|
rti[0].point = 0xa0;
|
|
rti[0].pm = toc->FirstTrack;
|
|
|
|
rti[1].adr_ctl = rti[0].adr_ctl;
|
|
rti[1].point = 0xa1;
|
|
rti[1].pm = toc->LastTrack;
|
|
|
|
rti[2].adr_ctl = rti[0].adr_ctl;
|
|
rti[2].point = 0xa2;
|
|
rti[2].pm = ct->Address[1];
|
|
rti[2].ps = ct->Address[2];
|
|
rti[2].pf = ct->Address[3];
|
|
|
|
ioctl->blocks_num = 3;
|
|
|
|
for (int i = 0; i < (ioctl->tracks_num - 1); i++) {
|
|
raw_track_info_t *crt = &(rti[ioctl->blocks_num]);
|
|
|
|
ct = &(toc->TrackData[i]);
|
|
|
|
crt->adr_ctl = ((ct->Adr & 0xf) << 4) | (ct->Control & 0xf);
|
|
crt->point = ct->TrackNumber;
|
|
crt->pm = ct->Address[1];
|
|
crt->ps = ct->Address[2];
|
|
crt->pf = ct->Address[3];
|
|
|
|
ioctl->blocks_num++;
|
|
}
|
|
} else if (status != 0) {
|
|
ioctl->blocks_num = (((cur_full_toc->Length[0] << 8) |
|
|
cur_full_toc->Length[1]) - 2) / 11;
|
|
memcpy(ioctl->cur_rti, cur_full_toc->Descriptors, ioctl->blocks_num * 11);
|
|
}
|
|
|
|
if (ioctl->blocks_num) for (int i = 0; i < ioctl->tracks_num; i++) {
|
|
const raw_track_info_t *crt = &(rti[i]);
|
|
|
|
if ((crt->point >= 1) && (crt->point <= 99) && !(crt->adr_ctl & 0x04)) {
|
|
ioctl->has_audio = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
uint8_t *u = (uint8_t *) cur_full_toc;
|
|
|
|
ioctl_log(ioctl->log, "%i blocks: %02X %02X %02X %02X\n",
|
|
ioctl->blocks_num, u[0], u[1], u[2], u[3]);
|
|
|
|
for (int i = 0; i < ioctl->blocks_num; i++) {
|
|
uint8_t *t = (uint8_t *) &rti[i];
|
|
ioctl_log(ioctl->log, "Block %03i: %02X %02X %02X %02X %02X %02X %02X %02X "
|
|
"%02X %02X %02X\n",
|
|
i, t[0], t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8],
|
|
t[9], t[10]);
|
|
}
|
|
#endif
|
|
|
|
free(cur_full_toc);
|
|
}
|
|
|
|
static void
|
|
ioctl_read_toc(ioctl_t *ioctl)
|
|
{
|
|
if (!ioctl->toc_valid) {
|
|
ioctl->toc_valid = 1;
|
|
(void) ioctl_read_normal_toc(ioctl, ioctl->cur_toc);
|
|
ioctl_read_raw_toc(ioctl);
|
|
}
|
|
}
|
|
|
|
static int
|
|
ioctl_get_track(const ioctl_t *ioctl, const uint32_t sector) {
|
|
raw_track_info_t *rti = (raw_track_info_t *) ioctl->cur_rti;
|
|
int track = -1;
|
|
|
|
for (int i = (ioctl->blocks_num - 1); i >= 0; i--) {
|
|
const raw_track_info_t *ct = &(rti[i]);
|
|
const uint32_t start = (ct->pm * 60 * 75) + (ct->ps * 75) + ct->pf - 150;
|
|
|
|
ioctl_log(ioctl->log, "ioctl_get_track(): ct: %02X, %08X\n",
|
|
ct->point, start);
|
|
|
|
if ((ct->point >= 1) && (ct->point <= 99) && (sector >= start)) {
|
|
track = i;
|
|
ioctl_log(ioctl->log, "ioctl_get_track(): found track: %i\n", i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return track;
|
|
}
|
|
|
|
static int
|
|
ioctl_is_track_audio(const ioctl_t *ioctl, const uint32_t pos)
|
|
{
|
|
const raw_track_info_t *rti = (const raw_track_info_t *) ioctl->cur_rti;
|
|
int ret = 0;
|
|
|
|
ioctl_read_toc((ioctl_t *) ioctl);
|
|
|
|
if (ioctl->has_audio && !ioctl->is_dvd) {
|
|
const int track = ioctl_get_track(ioctl, pos);
|
|
const int control = rti[track].adr_ctl;
|
|
|
|
ret = !(control & 0x04);
|
|
|
|
ioctl_log(ioctl->log, "ioctl_is_track_audio(%08X, %02X): %i\n", pos, track, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Shared functions. */
|
|
static int
|
|
ioctl_get_track_info(const void *local, const uint32_t track,
|
|
int end, track_info_t *ti)
|
|
{
|
|
const ioctl_t * ioctl = (const ioctl_t *) local;
|
|
const CDROM_TOC *toc = (const CDROM_TOC *) ioctl->cur_toc;
|
|
int ret = 1;
|
|
|
|
ioctl_read_toc((ioctl_t *) ioctl);
|
|
|
|
if ((track < 1) || (track == 0xaa) || (track > (toc->LastTrack + 1))) {
|
|
ioctl_log(ioctl->log, "ioctl_get_track_info(%02i)\n", track);
|
|
ret = 0;
|
|
} else {
|
|
const TRACK_DATA * td = &toc->TrackData[track - 1];
|
|
|
|
ti->m = td->Address[1];
|
|
ti->s = td->Address[2];
|
|
ti->f = td->Address[3];
|
|
|
|
ti->number = td->TrackNumber;
|
|
ti->attr = td->Control;
|
|
ti->attr |= ((td->Adr << 4) & 0xf0);
|
|
|
|
ioctl_log(ioctl->log, "ioctl_get_track_info(%02i): %02i:%02i:%02i, %02i, %02X\n",
|
|
track, ti->m, ti->s, ti->f, ti->number, ti->attr);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
ioctl_get_raw_track_info(const void *local, int *num, uint8_t *rti)
|
|
{
|
|
const ioctl_t *ioctl = (const ioctl_t *) local;
|
|
|
|
*num = ioctl->blocks_num;
|
|
memcpy(rti, ioctl->cur_rti, ioctl->blocks_num * 11);
|
|
}
|
|
|
|
static int
|
|
ioctl_is_track_pre(const void *local, const uint32_t sector)
|
|
{
|
|
const ioctl_t *ioctl = (const ioctl_t *) local;
|
|
const raw_track_info_t *rti = (const raw_track_info_t *) ioctl->cur_rti;
|
|
int ret = 0;
|
|
|
|
ioctl_read_toc((ioctl_t *) ioctl);
|
|
|
|
if (ioctl->has_audio && !ioctl->is_dvd) {
|
|
const int track = ioctl_get_track(ioctl, sector);
|
|
const int control = rti[track].adr_ctl;
|
|
|
|
ret = control & 0x01;
|
|
|
|
ioctl_log(ioctl->log, "ioctl_is_track_pre(%08X, %02X): %i\n", sector, track, ret);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ioctl_read_sector(const void *local, uint8_t *buffer, uint32_t const sector)
|
|
{
|
|
typedef struct SCSI_PASS_THROUGH_DIRECT_BUF {
|
|
SCSI_PASS_THROUGH_DIRECT spt;
|
|
ULONG Filler;
|
|
UCHAR SenseBuf[64];
|
|
} SCSI_PASS_THROUGH_DIRECT_BUF;
|
|
|
|
const ioctl_t * ioctl = (const ioctl_t *) local;
|
|
const raw_track_info_t * rti = (raw_track_info_t *) ioctl->cur_rti;
|
|
unsigned long int unused = 0;
|
|
const int sc_offs = (sector == 0xffffffff) ? 0 : 2352;
|
|
int len = (sector == 0xffffffff) ? 16 : 2368;
|
|
int m = 0;
|
|
int s = 0;
|
|
int f = 0;
|
|
uint32_t lba = sector;
|
|
int ret;
|
|
SCSI_PASS_THROUGH_DIRECT_BUF req;
|
|
|
|
ioctl_open_handle((ioctl_t *) ioctl);
|
|
|
|
if (ioctl->is_dvd) {
|
|
int track;
|
|
|
|
req.spt.DataTransferLength = 0;
|
|
ret = 0;
|
|
|
|
if (lba == 0xffffffff) {
|
|
lba = ioctl->dev->seek_pos;
|
|
track = ioctl_get_track(ioctl, lba);
|
|
|
|
if (track != -1) {
|
|
req.spt.DataTransferLength = len;
|
|
ret = 1;
|
|
}
|
|
} else {
|
|
len = COOKED_SECTOR_SIZE;
|
|
track = ioctl_get_track(ioctl, lba);
|
|
|
|
if (track != -1) {
|
|
DWORD newPos = SetFilePointer(ioctl->handle, (long) lba * COOKED_SECTOR_SIZE,
|
|
0, FILE_BEGIN);
|
|
|
|
if (newPos != 0xffffffff)
|
|
ret = ReadFile(ioctl->handle, &(buffer[16]),
|
|
COOKED_SECTOR_SIZE, (LPDWORD) &req.spt.DataTransferLength,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (ret && (req.spt.DataTransferLength >= len) && (track != -1)) {
|
|
const raw_track_info_t *ct = &(rti[track]);
|
|
const uint32_t start = (ct->pm * 60 * 75) + (ct->ps * 75) + ct->pf;
|
|
|
|
m = s = f = 0;
|
|
|
|
/* Construct sector header and sub-header. */
|
|
if (sector != 0xffffffff) {
|
|
/* Sync bytes. */
|
|
buffer[0] = 0x00;
|
|
memset(&(buffer[1]), 0xff, 10);
|
|
buffer[11] = 0x00;
|
|
|
|
/* Sector header. */
|
|
FRAMES_TO_MSF(lba + 150, &m, &s, &f);
|
|
buffer[12] = bin2bcd(m);
|
|
buffer[13] = bin2bcd(s);
|
|
buffer[14] = bin2bcd(f);
|
|
|
|
/* Mode 1 data. */
|
|
buffer[15] = 0x01;
|
|
}
|
|
|
|
/* Construct Q. */
|
|
buffer[sc_offs + 0] = (ct->adr_ctl >> 4) | ((ct->adr_ctl & 0xf) << 4);
|
|
buffer[sc_offs + 1] = bin2bcd(ct->point);
|
|
buffer[sc_offs + 2] = 1;
|
|
FRAMES_TO_MSF((int32_t) (lba + 150 - start), &m, &s, &f);
|
|
buffer[sc_offs + 3] = bin2bcd(m);
|
|
buffer[sc_offs + 4] = bin2bcd(s);
|
|
buffer[sc_offs + 5] = bin2bcd(f);
|
|
FRAMES_TO_MSF(lba + 150, &m, &s, &f);
|
|
buffer[sc_offs + 7] = bin2bcd(m);
|
|
buffer[sc_offs + 8] = bin2bcd(s);
|
|
buffer[sc_offs + 9] = bin2bcd(f);
|
|
}
|
|
} else {
|
|
memset(&req, 0x00, sizeof(SCSI_PASS_THROUGH_DIRECT_BUF));
|
|
req.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
|
req.spt.PathId = 0;
|
|
req.spt.TargetId = 1;
|
|
req.spt.Lun = 0;
|
|
req.spt.CdbLength = 12;
|
|
req.spt.DataIn = SCSI_IOCTL_DATA_IN;
|
|
req.spt.SenseInfoLength = sizeof(req.SenseBuf);
|
|
req.spt.DataTransferLength = len;
|
|
req.spt.TimeOutValue = 6;
|
|
req.spt.DataBuffer = buffer;
|
|
req.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_BUF, SenseBuf);
|
|
|
|
/* Fill in the CDB. */
|
|
req.spt.Cdb[0] = 0xbe; /* READ CD */
|
|
req.spt.Cdb[1] = 0x00;
|
|
req.spt.Cdb[2] = (sector >> 24) & 0xff;
|
|
req.spt.Cdb[3] = (sector >> 16) & 0xff;
|
|
req.spt.Cdb[4] = (sector >> 8) & 0xff;
|
|
req.spt.Cdb[5] = sector & 0xff; /* Starting Logical Block Address. */
|
|
req.spt.Cdb[6] = 0x00;
|
|
req.spt.Cdb[7] = 0x00;
|
|
req.spt.Cdb[8] = 0x01; /* Transfer Length. */
|
|
/* If sector is FFFFFFFF, only return the subchannel. */
|
|
req.spt.Cdb[9] = (sector == 0xffffffff) ? 0x00 : 0xf8;
|
|
req.spt.Cdb[10] = 0x02;
|
|
req.spt.Cdb[11] = 0x00;
|
|
DWORD length = sizeof(SCSI_PASS_THROUGH_DIRECT_BUF);
|
|
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
uint8_t *cdb = (uint8_t *) req.spt.Cdb;
|
|
ioctl_log(ioctl->log, "Host CDB: %02X %02X %02X %02X %02X %02X "
|
|
"%02X %02X %02X %02X %02X %02X\n",
|
|
ioctl->dev->id, cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5],
|
|
cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]);
|
|
#endif
|
|
|
|
ret = DeviceIoControl(ioctl->handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
&req, length,
|
|
&req, length, &unused, NULL);
|
|
}
|
|
|
|
ioctl_log(ioctl->log, "ioctl_read_sector: ret = %d, req.spt.DataTransferLength = %lu\n",
|
|
ret, req.spt.DataTransferLength);
|
|
ioctl_log(ioctl->log, "Sense: %08X, %08X\n", req.spt.SenseInfoLength, req.spt.SenseInfoOffset);
|
|
if (req.spt.SenseInfoLength >= 16) {
|
|
uint8_t *cdb = (uint8_t *) req.SenseBuf;
|
|
if ((cdb[2] == 0x03) && (cdb[12] == 0x11))
|
|
/* Treat this as an error to corectly indicate CIRC error to the guest. */
|
|
ret = 0;
|
|
ioctl_log(ioctl->log, "Host sense: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
cdb[0], cdb[1], cdb[ 2], cdb[ 3], cdb[ 4], cdb[ 5], cdb[ 6], cdb[ 7]);
|
|
ioctl_log(ioctl->log, " %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
cdb[8], cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], cdb[14], cdb[15]);
|
|
}
|
|
|
|
ret = (!!ret > 0) ? (req.spt.DataTransferLength >= len) : -1;
|
|
ioctl_log(ioctl->log, "iocl_read_sector: final ret = %i\n", ret);
|
|
|
|
/* Construct raw subchannel data from Q only. */
|
|
if ((ret > 0) && (req.spt.DataTransferLength >= len))
|
|
for (int i = 11; i >= 0; i--)
|
|
for (int j = 7; j >= 0; j--)
|
|
buffer[2352 + (i * 8) + j] = ((buffer[sc_offs + i] >> (7 - j)) & 0x01) << 6;
|
|
|
|
ioctl_close_handle((ioctl_t *) ioctl);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint8_t
|
|
ioctl_get_track_type(const void *local, const uint32_t sector)
|
|
{
|
|
ioctl_t * ioctl = (ioctl_t *) local;
|
|
int track = ioctl_get_track(ioctl, sector);
|
|
raw_track_info_t * rti = (raw_track_info_t *) ioctl->cur_rti;
|
|
const raw_track_info_t *trk = &(rti[track]);
|
|
uint8_t ret = 0x00;
|
|
|
|
if (ioctl_is_track_audio(ioctl, sector))
|
|
ret = CD_TRACK_AUDIO;
|
|
else if (track != -1) for (int i = 0; i < ioctl->blocks_num; i++) {
|
|
const raw_track_info_t *ct = &(rti[i]);
|
|
const raw_track_info_t *nt = &(rti[i + 1]);
|
|
|
|
if (ct->point == 0xa0) {
|
|
uint8_t first = ct->pm;
|
|
uint8_t last = nt->pm;
|
|
|
|
if ((trk->point >= first) && (trk->point <= last)) {
|
|
ret = ct->ps;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static uint32_t
|
|
ioctl_get_last_block(const void *local)
|
|
{
|
|
const ioctl_t *ioctl = (const ioctl_t *) local;
|
|
const CDROM_TOC *toc = (const CDROM_TOC *) ioctl->cur_toc;
|
|
uint32_t lb = 0;
|
|
|
|
ioctl_read_toc((ioctl_t *) ioctl);
|
|
|
|
for (int c = 0; c <= toc->LastTrack; c++) {
|
|
const TRACK_DATA *td = &toc->TrackData[c];
|
|
const uint32_t address = MSFtoLBA(td->Address[1], td->Address[2],
|
|
td->Address[3]) - 150;
|
|
|
|
if (address > lb)
|
|
lb = address;
|
|
}
|
|
|
|
ioctl_log(ioctl->log, "LBCapacity=%d\n", lb);
|
|
|
|
return lb;
|
|
}
|
|
|
|
static int
|
|
ioctl_read_dvd_structure(const void *local, const uint8_t layer, const uint8_t format,
|
|
uint8_t *buffer, uint32_t *info)
|
|
{
|
|
typedef struct SCSI_PASS_THROUGH_DIRECT_BUF {
|
|
SCSI_PASS_THROUGH_DIRECT spt;
|
|
ULONG Filler;
|
|
UCHAR SenseBuf[64];
|
|
} SCSI_PASS_THROUGH_DIRECT_BUF;
|
|
|
|
const ioctl_t * ioctl = (const ioctl_t *) local;
|
|
unsigned long int unused = 0;
|
|
const int len = 2052;
|
|
SCSI_PASS_THROUGH_DIRECT_BUF req;
|
|
|
|
ioctl_open_handle((ioctl_t *) ioctl);
|
|
|
|
memset(&req, 0x00, sizeof(SCSI_PASS_THROUGH_DIRECT_BUF));
|
|
req.spt.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
|
|
req.spt.PathId = 0;
|
|
req.spt.TargetId = 1;
|
|
req.spt.Lun = 0;
|
|
req.spt.CdbLength = 12;
|
|
req.spt.DataIn = SCSI_IOCTL_DATA_IN;
|
|
req.spt.SenseInfoLength = sizeof(req.SenseBuf);
|
|
req.spt.DataTransferLength = len;
|
|
req.spt.TimeOutValue = 6;
|
|
req.spt.DataBuffer = buffer;
|
|
req.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_BUF, SenseBuf);
|
|
|
|
/* Fill in the CDB. */
|
|
req.spt.Cdb[0] = 0xad;
|
|
req.spt.Cdb[1] = 0x00;
|
|
req.spt.Cdb[2] = 0x00;
|
|
req.spt.Cdb[3] = 0x00;
|
|
req.spt.Cdb[4] = 0x00;
|
|
req.spt.Cdb[5] = 0x00;
|
|
req.spt.Cdb[6] = layer; /* Layer Number */
|
|
req.spt.Cdb[7] = format; /* Format */
|
|
req.spt.Cdb[8] = 0x08; /* Allocation Length */
|
|
req.spt.Cdb[9] = 0x04;
|
|
req.spt.Cdb[10] = 0x00; /* AGID */
|
|
req.spt.Cdb[11] = 0x00;
|
|
|
|
DWORD length = sizeof(SCSI_PASS_THROUGH_DIRECT_BUF);
|
|
|
|
#ifdef ENABLE_IOCTL_LOG
|
|
uint8_t *cdb = (uint8_t *) req.spt.Cdb;
|
|
ioctl_log(ioctl->log, "Host CDB: %02X %02X %02X %02X %02X %02X "
|
|
"%02X %02X %02X %02X %02X %02X\n",
|
|
cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5],
|
|
cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11]);
|
|
#endif
|
|
|
|
int ret = DeviceIoControl(ioctl->handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
&req, length,
|
|
&req, length,
|
|
&unused, NULL);
|
|
|
|
ioctl_log(ioctl->log, "ioctl_read_dvd_structure(): ret = %d, "
|
|
"req.spt.DataTransferLength = %lu\n",
|
|
ret, req.spt.DataTransferLength);
|
|
ioctl_log(ioctl->log, "Sense: %08X, %08X\n", req.spt.SenseInfoLength,
|
|
req.spt.SenseInfoOffset);
|
|
|
|
if (req.spt.SenseInfoLength >= 16) {
|
|
uint8_t *sb = (uint8_t *) req.SenseBuf;
|
|
/* Return sense to the host as is. */
|
|
ret = -((sb[2] << 16) | (sb[12] << 8) | sb[13]);
|
|
if (info != NULL)
|
|
*info = *(uint32_t *) &(sb[3]);
|
|
ioctl_log(ioctl->log, "Host sense: %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
sb[0], sb[1], sb[ 2], sb[ 3], sb[ 4], sb[ 5], sb[ 6], sb[ 7]);
|
|
ioctl_log(ioctl->log, " %02X %02X %02X %02X %02X %02X %02X %02X\n",
|
|
sb[8], sb[9], sb[10], sb[11], sb[12], sb[13], sb[14], sb[15]);
|
|
} else
|
|
ret = ret ? (req.spt.DataTransferLength >= len) : 0;
|
|
|
|
ioctl_close_handle((ioctl_t *) ioctl);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
ioctl_is_dvd(const void *local)
|
|
{
|
|
const ioctl_t *ioctl = (const ioctl_t *) local;
|
|
|
|
return ioctl->is_dvd;
|
|
}
|
|
|
|
static int
|
|
ioctl_has_audio(const void *local)
|
|
{
|
|
const ioctl_t *ioctl = (const ioctl_t *) local;
|
|
|
|
return ioctl->has_audio;
|
|
}
|
|
|
|
static int
|
|
ioctl_ext_medium_changed(void *local)
|
|
{
|
|
ioctl_t * ioctl = (ioctl_t *) local;
|
|
const CDROM_TOC *toc = (CDROM_TOC *) ioctl->cur_toc;
|
|
const TRACK_DATA *ltd = &toc->TrackData[toc->LastTrack];
|
|
const uint32_t old_addr = *(uint32_t *) ltd->Address;
|
|
const int temp = ioctl_read_normal_toc(ioctl, ioctl->cur_toc);
|
|
int ret = 0;
|
|
|
|
if (temp == 1) {
|
|
if (ioctl->toc_valid && ((*(uint32_t *) ltd->Address) != old_addr)) {
|
|
/* The TOC has changed. */
|
|
ioctl->toc_valid = 0;
|
|
ret = 1;
|
|
}
|
|
|
|
if (!ioctl->toc_valid) {
|
|
ioctl->toc_valid = 1;
|
|
ioctl_read_raw_toc(ioctl);
|
|
}
|
|
} else {
|
|
/* There has been some kind of error - not a medium change, but a not ready
|
|
condition. */
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret == 1) {
|
|
if (ioctl->is_dvd)
|
|
ioctl->dev->cd_status = CD_STATUS_DVD;
|
|
else
|
|
ioctl->dev->cd_status = ioctl->has_audio ? CD_STATUS_STOPPED :
|
|
CD_STATUS_DATA_ONLY;
|
|
|
|
ioctl->dev->cdrom_capacity = ioctl_get_last_block(ioctl);
|
|
} else if (ret == -1)
|
|
ioctl->dev->cd_status = CD_STATUS_EMPTY;
|
|
|
|
ioctl_log(ioctl->log, "ioctl_ext_medium_changed(): %i\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
ioctl_close(void *local)
|
|
{
|
|
ioctl_t *ioctl = (ioctl_t *) local;
|
|
|
|
ioctl_close_handle(ioctl);
|
|
ioctl->handle = NULL;
|
|
|
|
ioctl_log(ioctl->log, "Log closed\n");
|
|
|
|
log_close(ioctl->log);
|
|
ioctl->log = NULL;
|
|
}
|
|
|
|
static const cdrom_ops_t ioctl_ops = {
|
|
ioctl_get_track_info,
|
|
ioctl_get_raw_track_info,
|
|
ioctl_is_track_pre,
|
|
ioctl_read_sector,
|
|
ioctl_get_track_type,
|
|
ioctl_get_last_block,
|
|
ioctl_read_dvd_structure,
|
|
ioctl_is_dvd,
|
|
ioctl_has_audio,
|
|
ioctl_ext_medium_changed,
|
|
ioctl_close
|
|
};
|
|
|
|
/* Public functions. */
|
|
void *
|
|
ioctl_open(cdrom_t *dev, const char *drv)
|
|
{
|
|
ioctl_t *ioctl = (ioctl_t *) calloc(1, sizeof(ioctl_t));
|
|
|
|
if (ioctl != NULL) {
|
|
char n[1024] = { 0 };
|
|
|
|
sprintf(n, "CD-ROM %i IOCtl", dev->id + 1);
|
|
ioctl->log = log_open(n);
|
|
|
|
memset(ioctl->path, 0x00, sizeof(ioctl->path));
|
|
|
|
wsprintf(ioctl->path, L"%S", &(drv[8]));
|
|
ioctl_log(ioctl->log, "Path is %S\n", ioctl->path);
|
|
|
|
ioctl->dev = dev;
|
|
ioctl->toc_valid = 0;
|
|
|
|
dev->ops = &ioctl_ops;
|
|
|
|
ioctl_load(ioctl);
|
|
}
|
|
|
|
return ioctl;
|
|
}
|