mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
1128 lines
30 KiB
C++
1128 lines
30 KiB
C++
/*
|
|
* UAE - The Un*x Amiga Emulator
|
|
*
|
|
* WIN32 CDROM/HD low level access code (SPTI)
|
|
*
|
|
* Copyright 2002-2010 Toni Wilen
|
|
*
|
|
*/
|
|
|
|
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
#include "options.h"
|
|
|
|
#ifdef WINDDK
|
|
|
|
#include "traps.h"
|
|
#include "memory.h"
|
|
#include "threaddep/thread.h"
|
|
#include "blkdev.h"
|
|
#include "scsidev.h"
|
|
#include "gui.h"
|
|
#ifdef RETROPLATFORM
|
|
#include "rp.h"
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <devioctl.h>
|
|
#include <ntddstor.h>
|
|
#include <winioctl.h>
|
|
#include <initguid.h> // Guid definition
|
|
#include <devguid.h> // Device guids
|
|
#include <setupapi.h> // for SetupDiXxx functions.
|
|
#include <ntddscsi.h>
|
|
#include <mmsystem.h>
|
|
|
|
#include "cda_play.h"
|
|
|
|
#define INQUIRY_SIZE 36
|
|
|
|
typedef struct _SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER {
|
|
SCSI_PASS_THROUGH_DIRECT spt;
|
|
ULONG Filler;
|
|
UCHAR SenseBuf[32];
|
|
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;
|
|
|
|
struct dev_info_spti {
|
|
TCHAR *drvpath;
|
|
TCHAR *name;
|
|
uae_u8 *inquirydata;
|
|
TCHAR *ident;
|
|
TCHAR drvletter;
|
|
TCHAR drvlettername[10];
|
|
int mediainserted;
|
|
HANDLE handle;
|
|
int isatapi;
|
|
int removable;
|
|
int type;
|
|
int bus, path, target, lun;
|
|
int scanmode;
|
|
uae_u8 *scsibuf;
|
|
bool open;
|
|
bool enabled;
|
|
struct device_info di;
|
|
struct cda_play cda;
|
|
uae_u8 sense[32];
|
|
int senselen;
|
|
};
|
|
|
|
static uae_sem_t scgp_sem;
|
|
static struct dev_info_spti dev_info[MAX_TOTAL_SCSI_DEVICES];
|
|
static int unittable[MAX_TOTAL_SCSI_DEVICES];
|
|
static int total_devices;
|
|
static int bus_open;
|
|
|
|
static int getunitnum (struct dev_info_spti *di)
|
|
{
|
|
if (!di)
|
|
return -1;
|
|
int idx = addrdiff(di, &dev_info[0]);
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
if (unittable[i] - 1 == idx)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
static struct dev_info_spti *unitcheck (int unitnum)
|
|
{
|
|
if (unitnum < 0 || unitnum >= MAX_TOTAL_SCSI_DEVICES)
|
|
return NULL;
|
|
if (unittable[unitnum] <= 0)
|
|
return NULL;
|
|
unitnum = unittable[unitnum] - 1;
|
|
// if (dev_info[unitnum].drvletter == 0)
|
|
// return NULL;
|
|
return &dev_info[unitnum];
|
|
}
|
|
|
|
static struct dev_info_spti *unitisopen (int unitnum)
|
|
{
|
|
struct dev_info_spti *di = unitcheck (unitnum);
|
|
if (!di)
|
|
return NULL;
|
|
if (di->open == false)
|
|
return NULL;
|
|
return di;
|
|
}
|
|
|
|
static int doscsi (struct dev_info_spti *di, int unitnum, SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER *swb, int *err)
|
|
{
|
|
DWORD status, returned;
|
|
|
|
*err = 0;
|
|
if (log_scsi) {
|
|
write_log (_T("SCSI, H=%X:%d:%d:%d:%d:\n"), di->handle, di->bus, di->path, di->target, di->lun);
|
|
scsi_log_before (swb->spt.Cdb, swb->spt.CdbLength,
|
|
swb->spt.DataIn == SCSI_IOCTL_DATA_OUT ? (uae_u8*)swb->spt.DataBuffer : NULL, swb->spt.DataTransferLength);
|
|
for (int i = 0; i < swb->spt.CdbLength; i++) {
|
|
if (i > 0)
|
|
write_log(_T("."));
|
|
write_log(_T("%02x"), swb->spt.Cdb[i]);
|
|
}
|
|
write_log(_T("\n"));
|
|
}
|
|
gui_flicker_led (LED_CD, unitnum, 1);
|
|
swb->spt.ScsiStatus = 0;
|
|
if (di->bus >= 0) {
|
|
swb->spt.PathId = di->path;
|
|
swb->spt.TargetId = di->target;
|
|
swb->spt.Lun = di->lun;
|
|
}
|
|
status = DeviceIoControl (di->handle, IOCTL_SCSI_PASS_THROUGH_DIRECT,
|
|
swb, sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
|
|
swb, sizeof (SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER),
|
|
&returned, NULL);
|
|
if (!status) {
|
|
int lasterror = GetLastError();
|
|
*err = lasterror;
|
|
write_log (_T("SCSI ERROR, H=%X:%d:%d:%d:%d: "), di->handle, di->bus, di->path, di->target, di->lun);
|
|
write_log (_T("Status = %d, Error code = %d, LastError=%d\n"), status, swb->spt.ScsiStatus, lasterror);
|
|
scsi_log_before (swb->spt.Cdb, swb->spt.CdbLength,
|
|
swb->spt.DataIn == SCSI_IOCTL_DATA_OUT ? (uae_u8*)swb->spt.DataBuffer : 0,swb->spt.DataTransferLength);
|
|
}
|
|
if (log_scsi)
|
|
scsi_log_after (swb->spt.DataIn == SCSI_IOCTL_DATA_IN ? (uae_u8*)swb->spt.DataBuffer : NULL, swb->spt.DataTransferLength,
|
|
swb->SenseBuf, swb->spt.SenseInfoLength);
|
|
if (swb->spt.SenseInfoLength > 0 && (swb->SenseBuf[2] == 0 || swb->SenseBuf[2] == 1))
|
|
swb->spt.SenseInfoLength = 0; /* 0 and 1 = success, not error.. */
|
|
if (swb->spt.SenseInfoLength > 0)
|
|
return 0;
|
|
gui_flicker_led (LED_CD, unitnum, 1);
|
|
return status;
|
|
}
|
|
|
|
|
|
#define MODE_SELECT_6 0x15
|
|
#define MODE_SENSE_6 0x1A
|
|
#define MODE_SELECT_10 0x55
|
|
#define MODE_SENSE_10 0x5A
|
|
|
|
static int execscsicmd (struct dev_info_spti *di, int unitnum, uae_u8 *data, int len, uae_u8 *inbuf, int inlen, int timeout, int *errp, int bypass)
|
|
{
|
|
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
|
|
DWORD status;
|
|
int err = 0;
|
|
int dolen;
|
|
|
|
if (!bypass) {
|
|
if (data[0] == 0x03 && di->senselen > 0) {
|
|
int l = di->senselen > inlen ? inlen : di->senselen;
|
|
memcpy(inbuf, di->sense, l);
|
|
di->senselen = 0;
|
|
return l;
|
|
}
|
|
int r = blkdev_is_audio_command(data[0]);
|
|
if (r > 0) {
|
|
di->senselen = sizeof(di->sense);
|
|
return blkdev_execute_audio_command(unitnum, data, len, inbuf, inlen, di->sense, &di->senselen);
|
|
} else if (r < 0) {
|
|
ciw_cdda_stop(&di->cda);
|
|
}
|
|
}
|
|
|
|
uae_sem_wait (&scgp_sem);
|
|
memset (&swb, 0, sizeof (swb));
|
|
swb.spt.Length = sizeof (SCSI_PASS_THROUGH);
|
|
swb.spt.CdbLength = len;
|
|
if (inbuf) {
|
|
swb.spt.DataIn = SCSI_IOCTL_DATA_IN;
|
|
swb.spt.DataTransferLength = inlen;
|
|
swb.spt.DataBuffer = inbuf;
|
|
memset (inbuf, 0, inlen);
|
|
} else {
|
|
swb.spt.DataIn = SCSI_IOCTL_DATA_OUT;
|
|
}
|
|
swb.spt.TimeOutValue = timeout < 0 ? 80 * 60 : (timeout == 0 ? 5 : timeout);
|
|
swb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseBuf);
|
|
swb.spt.SenseInfoLength = 32;
|
|
memcpy (swb.spt.Cdb, data, len);
|
|
status = doscsi (di, unitnum, &swb, &err);
|
|
uae_sem_post (&scgp_sem);
|
|
dolen = swb.spt.DataTransferLength;
|
|
if (errp)
|
|
*errp = err;
|
|
if (!status)
|
|
return -1;
|
|
return dolen;
|
|
}
|
|
|
|
static int read_block_cda(struct cda_play *cda, int unitnum, uae_u8 *data, int sector, int size, int sectorsize)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return 0;
|
|
if (sectorsize != 2352 + 96)
|
|
return 0;
|
|
uae_u8 cmd[12] = { 0xbe, 0, (uae_u8)(sector >> 24), (uae_u8)(sector >> 16), (uae_u8)(sector >> 8), (uae_u8)(sector >> 0), (uae_u8)(size >> 16), (uae_u8)(size >> 8), (uae_u8)(size >> 0), 0x10, 1, 0 };
|
|
int err = 0;
|
|
int len = execscsicmd(di, unitnum, cmd, sizeof(cmd), data, size * sectorsize, -1, &err, true);
|
|
if (len >= 0)
|
|
return len;
|
|
return -1;
|
|
}
|
|
|
|
static int execscsicmd_direct (int unitnum, struct amigascsi *as)
|
|
{
|
|
struct dev_info_spti *di = unitisopen (unitnum);
|
|
if (!di)
|
|
return -1;
|
|
|
|
if (as->cmd[0] == 0x03 && di->senselen > 0) {
|
|
memcpy(as->data, di->sense, di->senselen);
|
|
as->actual = di->senselen;
|
|
as->status = 0;
|
|
as->sactual = 0;
|
|
as->cmdactual = as->cmd_len;
|
|
di->senselen = 0;
|
|
return 0;
|
|
}
|
|
int r = blkdev_is_audio_command(as->cmd[0]);
|
|
if (r > 0) {
|
|
di->senselen = sizeof(di->sense);
|
|
int len = blkdev_execute_audio_command(unitnum, as->cmd, as->cmd_len, as->data, as->len, di->sense, &di->senselen);
|
|
as->actual = len;
|
|
as->cmdactual = as->cmd_len;
|
|
as->sactual = 0;
|
|
as->status = 0;
|
|
if (len < 0) {
|
|
/* copy sense? */
|
|
if (as->sense_len > 32)
|
|
as->sense_len = 32;
|
|
int senselen = (as->flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
|
|
(as->flags & 2) ? as->sense_len : /* SCSIF_AUTOSENSE */ 32;
|
|
if (senselen > 0) {
|
|
for (as->sactual = 0; as->sactual < di->senselen && as->sactual < senselen; as->sactual++) {
|
|
as->sensedata[as->sactual] = di->sense[as->sactual];
|
|
}
|
|
}
|
|
as->actual = 0;
|
|
as->status = 2;
|
|
return 45;
|
|
}
|
|
return 0;
|
|
} else if (r < 0) {
|
|
ciw_cdda_stop(&di->cda);
|
|
}
|
|
|
|
SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER swb;
|
|
DWORD status;
|
|
int sactual = 0, i;
|
|
int io_error = 0, err, parm;
|
|
uae_u8 *scsi_datap, *scsi_datap_org;
|
|
uae_u32 scsi_cmd_len_orig = as->cmd_len;
|
|
|
|
memset (&swb, 0, sizeof (swb));
|
|
swb.spt.Length = sizeof (SCSI_PASS_THROUGH);
|
|
swb.spt.SenseInfoOffset = offsetof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, SenseBuf);
|
|
|
|
if (as->len > DEVICE_SCSI_BUFSIZE)
|
|
as->len = DEVICE_SCSI_BUFSIZE;
|
|
|
|
uae_sem_wait (&scgp_sem);
|
|
|
|
/* the Amiga does not tell us how long the timeout shall be, so make it _very_ long (specified in seconds) */
|
|
swb.spt.TimeOutValue = 80 * 60;
|
|
scsi_datap = scsi_datap_org = as->len ? as->data : 0;
|
|
swb.spt.DataIn = (as->flags & 1) ? SCSI_IOCTL_DATA_IN : SCSI_IOCTL_DATA_OUT;
|
|
for (i = 0; i < as->cmd_len; i++)
|
|
swb.spt.Cdb[i] = as->cmd[i];
|
|
if (as->sense_len > 32)
|
|
as->sense_len = 32;
|
|
swb.spt.SenseInfoLength = (as->flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
|
|
(as->flags & 2) ? as->sense_len : /* SCSIF_AUTOSENSE */ 32;
|
|
if (dev_info[unitnum].isatapi)
|
|
scsi_atapi_fixup_pre (swb.spt.Cdb, &as->cmd_len, &scsi_datap, &as->len, &parm);
|
|
|
|
memcpy (di->scsibuf, scsi_datap, as->len);
|
|
|
|
swb.spt.CdbLength = (UCHAR)as->cmd_len;
|
|
swb.spt.DataTransferLength = as->len;
|
|
swb.spt.DataBuffer = di->scsibuf;
|
|
|
|
status = doscsi (di, unitnum, &swb, &err);
|
|
|
|
memcpy (scsi_datap, di->scsibuf, as->len);
|
|
|
|
as->cmdactual = status == 0 ? 0 : scsi_cmd_len_orig; /* fake scsi_CmdActual */
|
|
as->status = swb.spt.ScsiStatus; /* scsi_Status */
|
|
if (swb.spt.ScsiStatus) {
|
|
io_error = 45; /* HFERR_BadStatus */
|
|
/* copy sense? */
|
|
for (sactual = 0; sactual < as->sense_len && sactual < swb.spt.SenseInfoLength; sactual++)
|
|
as->sensedata[sactual] = swb.SenseBuf[sactual];
|
|
as->actual = 0; /* scsi_Actual */
|
|
} else {
|
|
for (int i = 0; i < as->sense_len; i++)
|
|
as->sensedata[i] = 0;
|
|
sactual = 0;
|
|
if (status == 0) {
|
|
io_error = 20; /* io_Error, but not specified */
|
|
as->actual = 0; /* scsi_Actual */
|
|
} else {
|
|
as->len = swb.spt.DataTransferLength;
|
|
if (dev_info[unitnum].isatapi)
|
|
scsi_atapi_fixup_post (swb.spt.Cdb, as->cmd_len, scsi_datap_org, scsi_datap, &as->len, parm);
|
|
io_error = 0;
|
|
as->actual = as->len; /* scsi_Actual */
|
|
}
|
|
}
|
|
as->sactual = sactual;
|
|
uae_sem_post (&scgp_sem);
|
|
|
|
if (scsi_datap != scsi_datap_org)
|
|
free (scsi_datap);
|
|
|
|
return io_error;
|
|
}
|
|
|
|
static uae_u8 *execscsicmd_out (int unitnum, uae_u8 *data, int len)
|
|
{
|
|
struct dev_info_spti *di = unitisopen (unitnum);
|
|
if (!di)
|
|
return 0;
|
|
int v = execscsicmd (di, unitnum, data, len, 0, 0, -1, NULL, false);
|
|
if (v < 0)
|
|
return 0;
|
|
return data;
|
|
}
|
|
|
|
static uae_u8 *execscsicmd_in (int unitnum, uae_u8 *data, int len, int *outlen)
|
|
{
|
|
struct dev_info_spti *di = unitisopen (unitnum);
|
|
if (!di)
|
|
return 0;
|
|
int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, -1, NULL, false);
|
|
if (v < 0)
|
|
return 0;
|
|
if (v == 0)
|
|
return 0;
|
|
if (outlen)
|
|
*outlen = v < *outlen ? v : *outlen;
|
|
return di->scsibuf;
|
|
}
|
|
|
|
static uae_u8 *execscsicmd_in_internal (struct dev_info_spti *di, int unitnum, uae_u8 *data, int len, int *outlen, int timeout)
|
|
{
|
|
int v = execscsicmd (di, unitnum, data, len, di->scsibuf, DEVICE_SCSI_BUFSIZE, timeout, NULL, false);
|
|
if (v < 0)
|
|
return 0;
|
|
if (v == 0)
|
|
return 0;
|
|
if (outlen)
|
|
*outlen = v;
|
|
return di->scsibuf;
|
|
}
|
|
|
|
static void close_scsi_device2 (struct dev_info_spti *di)
|
|
{
|
|
di->cda.subcodevalid = false;
|
|
if (di->open == false)
|
|
return;
|
|
di->open = false;
|
|
if (di->handle != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(di->handle);
|
|
uae_sem_destroy(&di->cda.sub_sem);
|
|
uae_sem_destroy(&di->cda.sub_sem2);
|
|
}
|
|
di->handle = INVALID_HANDLE_VALUE;
|
|
}
|
|
|
|
static void close_scsi_device (int unitnum)
|
|
{
|
|
struct dev_info_spti *di = unitisopen (unitnum);
|
|
if (!di)
|
|
return;
|
|
close_scsi_device2 (di);
|
|
blkdev_cd_change (unitnum, di->drvletter ? di->drvlettername : di->name);
|
|
unittable[unitnum] = 0;
|
|
}
|
|
|
|
static void free_scsi_device (struct dev_info_spti *di)
|
|
{
|
|
close_scsi_device2 (di);
|
|
xfree (di->name);
|
|
xfree (di->drvpath);
|
|
xfree (di->inquirydata);
|
|
VirtualFree (di->scsibuf, 0, MEM_RELEASE);
|
|
di->name = NULL;
|
|
di->drvpath = NULL;
|
|
di->inquirydata = NULL;
|
|
di->scsibuf = NULL;
|
|
memset (di, 0, sizeof (struct dev_info_spti));
|
|
}
|
|
|
|
static int rescan (void);
|
|
|
|
static void close_scsi_bus (void)
|
|
{
|
|
if (!bus_open) {
|
|
write_log (_T("SPTI close_bus() when already closed!\n"));
|
|
return;
|
|
}
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
free_scsi_device (&dev_info[i]);
|
|
unittable[i] = 0;
|
|
}
|
|
total_devices = 0;
|
|
bus_open = 0;
|
|
write_log (_T("SPTI driver closed.\n"));
|
|
}
|
|
|
|
static int open_scsi_bus (int flags)
|
|
{
|
|
if (bus_open) {
|
|
write_log (_T("SPTI open_bus() more than once!\n"));
|
|
return 1;
|
|
}
|
|
total_devices = 0;
|
|
uae_sem_init (&scgp_sem, 0, 1);
|
|
rescan ();
|
|
bus_open = 1;
|
|
write_log (_T("SPTI driver open, %d devices.\n"), total_devices);
|
|
return total_devices;
|
|
}
|
|
|
|
static int mediacheck (struct dev_info_spti *di, int unitnum)
|
|
{
|
|
uae_u8 cmd [6] = { 0,0,0,0,0,0 }; /* TEST UNIT READY */
|
|
if (di->open == false)
|
|
return -1;
|
|
int v = execscsicmd (di, unitnum, cmd, sizeof cmd, 0, 0, 0, NULL, false);
|
|
return v >= 0 ? 1 : 0;
|
|
}
|
|
|
|
static int mediacheck_full (struct dev_info_spti *di, int unitnum, struct device_info *dinfo)
|
|
{
|
|
uae_u8 cmd1[10] = { 0x25,0,0,0,0,0,0,0,0,0 }; /* READ CAPACITY */
|
|
uae_u8 *p;
|
|
int outlen;
|
|
|
|
dinfo->sectorspertrack = 0;
|
|
dinfo->trackspercylinder = 0;
|
|
dinfo->bytespersector = 0;
|
|
dinfo->cylinders = 0;
|
|
dinfo->write_protected = 1;
|
|
if (di->open == false)
|
|
return 0;
|
|
outlen = 32;
|
|
p = execscsicmd_in_internal (di, unitnum, cmd1, sizeof cmd1, &outlen, 0);
|
|
if (p && outlen >= 8) {
|
|
dinfo->bytespersector = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
|
|
dinfo->sectorspertrack = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]) + 1;
|
|
dinfo->trackspercylinder = 1;
|
|
dinfo->cylinders = 1;
|
|
}
|
|
if (di->type == INQ_DASD) {
|
|
uae_u8 cmd2[10] = { 0x5a,0x08,0,0,0,0,0,0,0xf0,0 }; /* MODE SENSE */
|
|
outlen = 32;
|
|
p = execscsicmd_in_internal (di, unitnum, cmd2, sizeof cmd2, &outlen, 0);
|
|
if (p && outlen >= 4) {
|
|
dinfo->write_protected = (p[3] & 0x80) ? 1 : 0;
|
|
}
|
|
}
|
|
sys_command_cd_toc(unitnum, &di->di.toc);
|
|
// write_log (_T("mediacheck_full(%d,%d,%d,%d,%d)\n"),
|
|
// di->bytespersector, di->sectorspertrack, di->trackspercylinder, di->cylinders, di->write_protected);
|
|
return 1;
|
|
}
|
|
|
|
static void update_device_info (int unitnum)
|
|
{
|
|
struct dev_info_spti *dispti = unitisopen (unitnum);
|
|
if (!dispti)
|
|
return;
|
|
struct device_info *di = &dispti->di;
|
|
_tcscpy (di->label, dispti->drvletter ? dispti->drvlettername : dispti->name);
|
|
_tcscpy (di->mediapath, dispti->drvpath);
|
|
di->bus = 0;
|
|
di->target = unitnum;
|
|
di->lun = 0;
|
|
di->media_inserted = mediacheck (dispti, unitnum);
|
|
di->removable = dispti->removable;
|
|
mediacheck_full (dispti, unitnum, di);
|
|
di->type = dispti->type;
|
|
di->unitnum = unitnum + 1;
|
|
di->backend = _T("SPTI");
|
|
if (log_scsi) {
|
|
write_log (_T("MI=%d TP=%d WP=%d CY=%d BK=%d RMB=%d '%s'\n"),
|
|
di->media_inserted, di->type, di->write_protected, di->cylinders, di->bytespersector, di->removable, di->label);
|
|
}
|
|
}
|
|
|
|
static void checkcapabilities (struct dev_info_spti *di)
|
|
{
|
|
STORAGE_ADAPTER_DESCRIPTOR desc;
|
|
STORAGE_PROPERTY_QUERY query;
|
|
DWORD ret, status;
|
|
|
|
memset (&query, 0, sizeof STORAGE_PROPERTY_QUERY);
|
|
query.PropertyId = StorageAdapterProperty;
|
|
query.QueryType = PropertyStandardQuery;
|
|
status = DeviceIoControl (di->handle, IOCTL_STORAGE_QUERY_PROPERTY,
|
|
&query, sizeof query, &desc, sizeof desc, &ret, NULL);
|
|
if (status) {
|
|
if (desc.Size > offsetof (STORAGE_ADAPTER_DESCRIPTOR, BusType))
|
|
write_log (_T("SCSI CAPS: BusType=%d, MaxTransfer=0x%08X, Mask=0x%08X\n"),
|
|
desc.BusType, desc.MaximumTransferLength, desc.AlignmentMask);
|
|
}
|
|
}
|
|
|
|
static int inquiry (struct dev_info_spti *di, int unitnum, uae_u8 *inquirydata)
|
|
{
|
|
uae_u8 cmd[6] = { 0x12,0,0,0,36,0 }; /* INQUIRY */
|
|
int outlen = INQUIRY_SIZE;
|
|
uae_u8 *p = execscsicmd_in_internal (di, unitnum, cmd, sizeof (cmd), &outlen, 0);
|
|
int inqlen = 0;
|
|
|
|
di->isatapi = 0;
|
|
di->removable = 0;
|
|
di->type = 0x1f;
|
|
if (!p) {
|
|
if (log_scsi)
|
|
write_log (_T("SPTI: INQUIRY failed\n"));
|
|
return 0;
|
|
}
|
|
inqlen = outlen > INQUIRY_SIZE ? INQUIRY_SIZE : outlen;
|
|
if (outlen >= 1) {
|
|
di->type = p[0] & 31;
|
|
di->removable = (p[1] & 0x80) ? 1 : 0;
|
|
}
|
|
if (outlen >= 2 && (p[0] & 31) == 5 && (p[2] & 7) == 0)
|
|
di->isatapi = 1;
|
|
memcpy (inquirydata, p, inqlen);
|
|
if (log_scsi) {
|
|
if (outlen >= INQUIRY_SIZE) {
|
|
char tmp[20];
|
|
TCHAR *s1, *s2;
|
|
|
|
memcpy (tmp, p + 8, 8);
|
|
tmp[8] = 0;
|
|
s1 = au (tmp);
|
|
memcpy (tmp, p + 16, 16);
|
|
tmp[16] = 0;
|
|
s2 = au (tmp);
|
|
write_log (_T("SPTI: INQUIRY: %02X%02X%02X %d '%s' '%s'\n"),
|
|
p[0], p[1], p[2], di->isatapi, s1, s2);
|
|
xfree (s2);
|
|
xfree (s1);
|
|
}
|
|
}
|
|
return inqlen;
|
|
}
|
|
|
|
static int open_scsi_device2 (struct dev_info_spti *di, int unitnum)
|
|
{
|
|
HANDLE h;
|
|
TCHAR *dev;
|
|
|
|
if (di->bus >= 0) {
|
|
dev = xmalloc (TCHAR, 100);
|
|
_stprintf (dev, _T("\\\\.\\Scsi%d:"), di->bus);
|
|
} else {
|
|
dev = my_strdup (di->drvpath);
|
|
}
|
|
if (!di->scsibuf)
|
|
di->scsibuf = (uae_u8*)VirtualAlloc (NULL, DEVICE_SCSI_BUFSIZE, MEM_COMMIT, PAGE_READWRITE);
|
|
h = CreateFile(dev,GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL);
|
|
di->handle = h;
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
write_log (_T("SPTI: failed to open unit %d err=%d ('%s')\n"), unitnum, GetLastError (), dev);
|
|
} else {
|
|
int err = 0;
|
|
uae_u8 inqdata[INQUIRY_SIZE + 1] = { 0 };
|
|
checkcapabilities (di);
|
|
execscsicmd(di, unitnum, inqdata, 6, NULL, 0, 0, &err, false);
|
|
if (err) {
|
|
write_log(_T("SPTI: TUR failed unit %d, err=%d ('%s':%d:%d:%d:%d)\n"), unitnum, err, dev,
|
|
di->bus, di->path, di->target, di->lun);
|
|
close_scsi_device2(di);
|
|
xfree(dev);
|
|
return 0;
|
|
}
|
|
if (!inquiry (di, unitnum, inqdata)) {
|
|
write_log (_T("SPTI: inquiry failed unit %d ('%s':%d:%d:%d:%d)\n"), unitnum, dev,
|
|
di->bus, di->path, di->target, di->lun);
|
|
close_scsi_device2 (di);
|
|
xfree (dev);
|
|
return 0;
|
|
}
|
|
inqdata[INQUIRY_SIZE] = 0;
|
|
di->name = my_strdup_ansi ((char*)inqdata + 8);
|
|
if (di->type == INQ_ROMD) {
|
|
// This fake CD device hangs if it sees SCSI read command.
|
|
if (!memcmp(inqdata + 8, "HUAWEI Mass Storage ", 8 + 16)) {
|
|
write_log(_T("SPTI: '%s' ignored.\n"), di->name);
|
|
close_scsi_device2(di);
|
|
xfree(dev);
|
|
return 0;
|
|
}
|
|
di->mediainserted = mediacheck (di, unitnum);
|
|
write_log (_T("SPTI: unit %d (%c:\\) opened [%s], %s, '%s'\n"),
|
|
unitnum, di->drvletter ? di->drvletter : '*',
|
|
di->isatapi ? _T("ATAPI") : _T("SCSI"),
|
|
di->mediainserted ? _T("media inserted") : _T("drive empty"),
|
|
di->name);
|
|
} else {
|
|
write_log (_T("SPTI: unit %d, type %d, '%s'\n"),
|
|
unitnum, di->type, di->name);
|
|
}
|
|
di->inquirydata = xmalloc (uae_u8, INQUIRY_SIZE);
|
|
memcpy (di->inquirydata, inqdata, INQUIRY_SIZE);
|
|
xfree (dev);
|
|
di->open = true;
|
|
update_device_info (unitnum);
|
|
if (di->type == INQ_ROMD)
|
|
blkdev_cd_change (unitnum, di->drvletter ? di->drvlettername : di->name);
|
|
di->cda.cdda_volume[0] = 0x7fff;
|
|
di->cda.cdda_volume[1] = 0x7fff;
|
|
uae_sem_init(&di->cda.sub_sem, 0, 1);
|
|
uae_sem_init(&di->cda.sub_sem2, 0, 1);
|
|
di->cda.cd_last_pos = 150;
|
|
return 1;
|
|
}
|
|
xfree (dev);
|
|
return 0;
|
|
}
|
|
|
|
int open_scsi_device (int unitnum, const TCHAR *ident, int flags)
|
|
{
|
|
struct dev_info_spti *di = NULL;
|
|
if (ident && ident[0]) {
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
di = &dev_info[i];
|
|
if (unittable[i] == 0 && di->drvletter != 0) {
|
|
if (!_tcsicmp (di->drvlettername, ident)) {
|
|
unittable[unitnum] = i + 1;
|
|
if (open_scsi_device2 (di, unitnum))
|
|
return 1;
|
|
unittable[unitnum] = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
di = &dev_info[unitnum];
|
|
for (int i = 0; i < MAX_TOTAL_SCSI_DEVICES; i++) {
|
|
if (unittable[i] == unitnum + 1)
|
|
return 0;
|
|
}
|
|
if (di->enabled == 0)
|
|
return 0;
|
|
unittable[unitnum] = unitnum + 1;
|
|
if (open_scsi_device2 (di, unitnum))
|
|
return 1;
|
|
unittable[unitnum] = 0;
|
|
return 0;
|
|
}
|
|
|
|
static int adddrive (const TCHAR *drvpath, int bus, int pathid, int targetid, int lunid, int scanmode)
|
|
{
|
|
struct dev_info_spti *di;
|
|
int cnt = total_devices, i;
|
|
int freeit = 1;
|
|
|
|
if (cnt >= MAX_TOTAL_SCSI_DEVICES)
|
|
return 0;
|
|
for (i = 0; i < total_devices; i++) {
|
|
di = &dev_info[i];
|
|
if (!_tcscmp (drvpath, di->drvpath))
|
|
return 0;
|
|
}
|
|
write_log (_T("SPTI: unit %d '%s' added\n"), total_devices, drvpath);
|
|
di = &dev_info[total_devices];
|
|
di->drvpath = my_strdup (drvpath);
|
|
di->type = 0;
|
|
di->bus = bus;
|
|
di->path = pathid;
|
|
di->target = targetid;
|
|
di->lun = lunid;
|
|
di->scanmode = scanmode;
|
|
di->drvletter = 0;
|
|
di->enabled = true;
|
|
|
|
for (TCHAR drvletter = 'C'; drvletter <= 'Z'; drvletter++) {
|
|
TCHAR drvname[10];
|
|
TCHAR volname[MAX_DPATH], volname2[MAX_DPATH];
|
|
_stprintf (drvname, _T("%c:\\"), drvletter);
|
|
if (GetVolumeNameForVolumeMountPoint (drvname, volname, sizeof volname / sizeof (TCHAR))) {
|
|
TCHAR drvpath2[MAX_DPATH];
|
|
_stprintf (drvpath2, _T("%s\\"), di->drvpath);
|
|
if (GetVolumeNameForVolumeMountPoint (drvpath2, volname2, sizeof volname2 / sizeof (TCHAR))) {
|
|
if (!_tcscmp (volname, volname2)) {
|
|
di->drvletter = drvletter;
|
|
_tcscpy (di->drvlettername, drvname);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
total_devices++;
|
|
unittable[cnt] = cnt + 1;
|
|
if (open_scsi_device2 (&dev_info[cnt], cnt)) {
|
|
for (i = 0; i < cnt; i++) {
|
|
if (!memcmp (di->inquirydata, dev_info[i].inquirydata, INQUIRY_SIZE) && di->scanmode != dev_info[i].scanmode) {
|
|
write_log (_T("duplicate device, skipped..\n"));
|
|
break;
|
|
}
|
|
}
|
|
if (i == cnt) {
|
|
freeit = 0;
|
|
close_scsi_device2 (&dev_info[cnt]);
|
|
}
|
|
}
|
|
if (freeit) {
|
|
free_scsi_device (&dev_info[cnt]);
|
|
total_devices--;
|
|
}
|
|
unittable[cnt] = 0;
|
|
return 1;
|
|
}
|
|
|
|
static struct device_info *info_device (int unitnum, struct device_info *di, int quick, int session)
|
|
{
|
|
struct dev_info_spti *dispti = unitcheck (unitnum);
|
|
if (!dispti)
|
|
return NULL;
|
|
if (!quick)
|
|
update_device_info (unitnum);
|
|
dispti->di.open = dispti->open;
|
|
memcpy (di, &dispti->di, sizeof (struct device_info));
|
|
return di;
|
|
}
|
|
|
|
bool win32_spti_media_change (TCHAR driveletter, int insert)
|
|
{
|
|
for (int i = 0; i < total_devices; i++) {
|
|
struct dev_info_spti *di = &dev_info[i];
|
|
if (di->drvletter == driveletter && di->mediainserted != insert) {
|
|
write_log (_T("SPTI: media change %c %d\n"), dev_info[i].drvletter, insert);
|
|
di->mediainserted = insert;
|
|
int unitnum = getunitnum (di);
|
|
if (unitnum >= 0) {
|
|
update_device_info (unitnum);
|
|
scsi_do_disk_change (unitnum, insert, NULL);
|
|
filesys_do_disk_change (unitnum, insert != 0);
|
|
blkdev_cd_change (unitnum, di->drvletter ? di->drvlettername : di->name);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static int check_isatapi (int unitnum)
|
|
{
|
|
struct dev_info_spti *di = unitcheck (unitnum);
|
|
if (!di)
|
|
return 0;
|
|
return di->isatapi;
|
|
}
|
|
|
|
static int getCDROMProperty (int idx, HDEVINFO DevInfo, const GUID *guid)
|
|
{
|
|
SP_DEVICE_INTERFACE_DATA interfaceData;
|
|
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData = NULL;
|
|
DWORD interfaceDetailDataSize, reqSize;
|
|
DWORD status, errorCode;
|
|
|
|
interfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
|
|
status = SetupDiEnumDeviceInterfaces (
|
|
DevInfo, // Interface Device Info handle
|
|
0, // Device Info data
|
|
guid, // Interface registered by driver
|
|
idx, // Member
|
|
&interfaceData // Device Interface Data
|
|
);
|
|
if (status == FALSE)
|
|
return FALSE;
|
|
|
|
status = SetupDiGetDeviceInterfaceDetail (
|
|
DevInfo, // Interface Device info handle
|
|
&interfaceData, // Interface data for the event class
|
|
NULL, // Checking for buffer size
|
|
0, // Checking for buffer size
|
|
&reqSize, // Buffer size required to get the detail data
|
|
NULL // Checking for buffer size
|
|
);
|
|
|
|
if (status == FALSE) {
|
|
errorCode = GetLastError();
|
|
if (errorCode != ERROR_INSUFFICIENT_BUFFER)
|
|
return FALSE;
|
|
}
|
|
|
|
interfaceDetailDataSize = reqSize;
|
|
interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)xmalloc (uae_u8, interfaceDetailDataSize);
|
|
if (interfaceDetailData == NULL)
|
|
return FALSE;
|
|
interfaceDetailData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
|
|
|
|
status = SetupDiGetDeviceInterfaceDetail (
|
|
DevInfo, // Interface Device info handle
|
|
&interfaceData, // Interface data for the event class
|
|
interfaceDetailData, // Interface detail data
|
|
interfaceDetailDataSize,// Interface detail data size
|
|
&reqSize, // Buffer size required to get the detail data
|
|
NULL); // Interface device info
|
|
|
|
if (status == FALSE)
|
|
return FALSE;
|
|
|
|
adddrive (interfaceDetailData->DevicePath, -1, -1, -1, -1, 1);
|
|
|
|
free (interfaceDetailData);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define SCSI_INFO_BUFFER_SIZE 0x5000
|
|
static void scanscsi (void)
|
|
{
|
|
PSCSI_BUS_DATA BusData;
|
|
PSCSI_INQUIRY_DATA InquiryData;
|
|
PSCSI_ADAPTER_BUS_INFO AdapterInfo;
|
|
HANDLE h;
|
|
BOOL status;
|
|
BOOL Claimed;
|
|
ULONG returnedLength;
|
|
SHORT Bus, Luns;
|
|
DWORD bytesTransferred;
|
|
int idx;
|
|
TCHAR DeviceName[256];
|
|
|
|
AdapterInfo = (PSCSI_ADAPTER_BUS_INFO)xmalloc (uae_u8, SCSI_INFO_BUFFER_SIZE);
|
|
if (AdapterInfo == NULL)
|
|
return;
|
|
|
|
idx = 0;
|
|
for (;;) {
|
|
_stprintf (DeviceName, _T("\\\\.\\Scsi%d:"), idx++);
|
|
h = CreateFile (DeviceName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0,
|
|
NULL, // no SECURITY_ATTRIBUTES structure
|
|
OPEN_EXISTING, // No special create flags
|
|
0, // No special attributes
|
|
NULL);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
xfree(AdapterInfo);
|
|
return;
|
|
}
|
|
|
|
if(!DeviceIoControl (h,
|
|
IOCTL_SCSI_RESCAN_BUS,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0,
|
|
&bytesTransferred,
|
|
NULL)) {
|
|
write_log (_T("Rescan SCSI port %d failed [Error %d]\n"), idx - 1, GetLastError());
|
|
CloseHandle (h);
|
|
continue;
|
|
}
|
|
|
|
// Get the SCSI inquiry data for all devices for the given SCSI bus
|
|
status = DeviceIoControl (
|
|
h,
|
|
IOCTL_SCSI_GET_INQUIRY_DATA,
|
|
NULL,
|
|
0,
|
|
AdapterInfo,
|
|
SCSI_INFO_BUFFER_SIZE,
|
|
&returnedLength,
|
|
NULL);
|
|
|
|
if (!status) {
|
|
write_log (_T("Error in IOCTL_SCSI_GET_INQUIRY_DATA\n") );
|
|
CloseHandle (h);
|
|
continue;
|
|
}
|
|
|
|
for (Bus = 0; Bus < AdapterInfo->NumberOfBuses; Bus++) {
|
|
int luncheck = 0;
|
|
BusData = &AdapterInfo->BusData[Bus];
|
|
InquiryData = (PSCSI_INQUIRY_DATA) ((PUCHAR)AdapterInfo + BusData->InquiryDataOffset);
|
|
for (Luns = 0; Luns < BusData->NumberOfLogicalUnits; Luns++) {
|
|
TCHAR label[100];
|
|
int type = InquiryData->InquiryData[0] & 0x1f;
|
|
Claimed = InquiryData->DeviceClaimed;
|
|
write_log (_T("SCSI=%d Initiator=%d Path=%d Target=%d LUN=%d Claimed=%s Type=%d\n"),
|
|
idx - 1,
|
|
BusData->InitiatorBusId, InquiryData->PathId, InquiryData->TargetId,
|
|
InquiryData->Lun, Claimed ? _T("Yes") : _T("No "), type);
|
|
if (Claimed == 0 && !luncheck) {
|
|
luncheck = 1;
|
|
_stprintf (label, _T("SCSI(%d):%d:%d:%d:%d"), idx - 1, BusData->InitiatorBusId,
|
|
InquiryData->PathId, InquiryData->TargetId, InquiryData->Lun);
|
|
adddrive (label, idx - 1, InquiryData->PathId, InquiryData->TargetId, InquiryData->Lun, 3);
|
|
}
|
|
InquiryData = (PSCSI_INQUIRY_DATA) ((PUCHAR)AdapterInfo + InquiryData->NextInquiryDataOffset);
|
|
} // for Luns
|
|
} // for Bus
|
|
CloseHandle (h);
|
|
}
|
|
}
|
|
|
|
static const GUID *guids[] = {
|
|
&GUID_DEVINTERFACE_CDROM,
|
|
&GUID_DEVCLASS_IMAGE,
|
|
&GUID_DEVCLASS_TAPEDRIVE,
|
|
NULL };
|
|
static const TCHAR *scsinames[] = { _T("Tape"), _T("Scanner"), _T("Changer"), NULL };
|
|
|
|
static int rescan (void)
|
|
{
|
|
int idx, idx2;
|
|
|
|
for (idx2 = 0; guids[idx2]; idx2++) {
|
|
HDEVINFO hDevInfo = SetupDiGetClassDevs(
|
|
guids[idx2],
|
|
NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
|
|
if (hDevInfo != INVALID_HANDLE_VALUE) {
|
|
for (idx = 0; ; idx++) {
|
|
if (!getCDROMProperty (idx, hDevInfo, guids[idx2]))
|
|
break;
|
|
}
|
|
SetupDiDestroyDeviceInfoList (hDevInfo);
|
|
}
|
|
}
|
|
|
|
for (idx2 = 0; scsinames[idx2]; idx2++) {
|
|
int max = 10;
|
|
for (idx = 0; idx < max; idx++) {
|
|
TCHAR tmp[100];
|
|
HANDLE h;
|
|
_stprintf (tmp, _T("\\\\.\\%s%d"), scsinames[idx2], idx);
|
|
h = CreateFile (tmp, GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL, OPEN_EXISTING, 0, NULL);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
adddrive (tmp, -1, -1, -1, -1, 2);
|
|
CloseHandle (h);
|
|
if (idx == max - 1)
|
|
max++;
|
|
}
|
|
}
|
|
}
|
|
if (currprefs.win32_uaescsimode == UAESCSI_SPTISCAN) {
|
|
write_log (_T("SCSI adapter enumeration..\n"));
|
|
scanscsi ();
|
|
write_log (_T("SCSI adapter enumeration ends\n"));
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
|
|
/* pause/unpause CD audio */
|
|
static int ioctl_command_pause(int unitnum, int paused)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return -1;
|
|
int old = di->cda.cdda_paused;
|
|
if ((paused && di->cda.cdda_play) || !paused)
|
|
di->cda.cdda_paused = paused;
|
|
return old;
|
|
}
|
|
|
|
|
|
/* stop CD audio */
|
|
static int ioctl_command_stop(int unitnum)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return 0;
|
|
|
|
ciw_cdda_stop(&di->cda);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static uae_u32 ioctl_command_volume(int unitnum, uae_u16 volume_left, uae_u16 volume_right)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return -1;
|
|
uae_u32 old = (di->cda.cdda_volume[1] << 16) | (di->cda.cdda_volume[0] << 0);
|
|
di->cda.cdda_volume[0] = volume_left;
|
|
di->cda.cdda_volume[1] = volume_right;
|
|
return old;
|
|
}
|
|
|
|
/* play CD audio */
|
|
static int ioctl_command_play(int unitnum, int startlsn, int endlsn, int scan, play_status_callback statusfunc, play_subchannel_callback subfunc)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return 0;
|
|
|
|
di->cda.di = &di->di;
|
|
di->cda.subcodevalid = false;
|
|
di->cda.cdda_play_finished = 0;
|
|
di->cda.cdda_subfunc = subfunc;
|
|
di->cda.cdda_statusfunc = statusfunc;
|
|
di->cda.cdda_scan = scan > 0 ? 10 : (scan < 0 ? 10 : 0);
|
|
di->cda.cdda_delay = ciw_cdda_setstate(&di->cda, -1, -1);
|
|
di->cda.cdda_delay_frames = ciw_cdda_setstate(&di->cda, -2, -1);
|
|
ciw_cdda_setstate(&di->cda, AUDIO_STATUS_NOT_SUPPORTED, -1);
|
|
di->cda.read_block = read_block_cda;
|
|
|
|
if (!isaudiotrack(&di->di.toc, startlsn)) {
|
|
ciw_cdda_setstate(&di->cda, AUDIO_STATUS_PLAY_ERROR, -1);
|
|
return 0;
|
|
}
|
|
if (!di->cda.cdda_play) {
|
|
uae_start_thread(_T("ioctl_cdda_play"), ciw_cdda_play, &di->cda, NULL);
|
|
}
|
|
di->cda.cdda_start = startlsn;
|
|
di->cda.cdda_end = endlsn;
|
|
di->cda.cd_last_pos = di->cda.cdda_start;
|
|
di->cda.cdda_play++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void sub_deinterleave(const uae_u8 *s, uae_u8 *d)
|
|
{
|
|
for (int i = 0; i < 8 * 12; i++) {
|
|
int dmask = 0x80;
|
|
int smask = 1 << (7 - (i / 12));
|
|
(*d) = 0;
|
|
for (int j = 0; j < 8; j++) {
|
|
(*d) |= (s[(i % 12) * 8 + j] & smask) ? dmask : 0;
|
|
dmask >>= 1;
|
|
}
|
|
d++;
|
|
}
|
|
}
|
|
|
|
/* read qcode */
|
|
static int ioctl_command_qcode(int unitnum, uae_u8 *buf, int sector, bool all)
|
|
{
|
|
struct dev_info_spti *di = unitisopen(unitnum);
|
|
if (!di)
|
|
return 0;
|
|
struct cda_play *cd = &di->cda;
|
|
|
|
if (all)
|
|
return 0;
|
|
|
|
memset(buf, 0, SUBQ_SIZE);
|
|
uae_u8 *p = buf;
|
|
|
|
int status = AUDIO_STATUS_NO_STATUS;
|
|
if (di->cda.cdda_play) {
|
|
status = AUDIO_STATUS_IN_PROGRESS;
|
|
if (di->cda.cdda_paused)
|
|
status = AUDIO_STATUS_PAUSED;
|
|
} else if (di->cda.cdda_play_finished) {
|
|
status = AUDIO_STATUS_PLAY_COMPLETE;
|
|
}
|
|
|
|
p[1] = status;
|
|
p[3] = 12;
|
|
|
|
p = buf + 4;
|
|
|
|
if (cd->subcodevalid) {
|
|
uae_sem_wait(&di->cda.sub_sem2);
|
|
uae_u8 subbuf[SUB_CHANNEL_SIZE];
|
|
sub_deinterleave(di->cda.subcodebuf, subbuf);
|
|
memcpy(p, subbuf + 12, 12);
|
|
uae_sem_post(&di->cda.sub_sem2);
|
|
} else {
|
|
int pos = di->cda.cd_last_pos;
|
|
int trk = cdtracknumber(&di->di.toc, pos);
|
|
if (trk >= 0) {
|
|
struct cd_toc *t = &di->di.toc.toc[trk];
|
|
p[0] = (t->control << 4) | (t->adr << 0);
|
|
p[1] = tobcd(trk);
|
|
p[2] = tobcd(1);
|
|
uae_u32 msf = lsn2msf(pos);
|
|
tolongbcd(p + 7, msf);
|
|
msf = lsn2msf(pos - t->paddress - 150);
|
|
tolongbcd(p + 3, msf);
|
|
} else {
|
|
p[1] = AUDIO_STATUS_NO_STATUS;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
|
|
struct device_functions devicefunc_scsi_spti = {
|
|
_T("SPTI"),
|
|
open_scsi_bus, close_scsi_bus, open_scsi_device, close_scsi_device, info_device,
|
|
execscsicmd_out, execscsicmd_in, execscsicmd_direct,
|
|
ioctl_command_pause, ioctl_command_stop, ioctl_command_play, ioctl_command_volume, ioctl_command_qcode,
|
|
0, 0, 0, 0, check_isatapi, 0, 0
|
|
};
|