static uae_u8 *execscsicmd_out_ioctl (int unitnum, uae_u8 *cmd_data, int cmd_len)
{
    struct scsidevdata *sdd;
    struct cdrom_generic_command cmd;
    int io_error;

    DEBUG_LOG ("SCSIDEV: unit = %d, execscsicmd_out_ioctl\n", unitnum);
    if (unitnum >= total_drives) {
	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
	return 0;
    }
    sdd = &drives[unitnum];
    if (cmd_len > CDROM_PACKET_SIZE) {
	DEBUG_LOG ("SCSIDEV: cmd_len too large (%d)\n", cmd_len);
	return 0;
    }
    memcpy (cmd.cmd, cmd_data, cmd_len);
    cmd.buffer = 0;
    cmd.buflen = 0;
    cmd.stat = 0;
    cmd.sense = 0;
    cmd.data_direction = CGC_DATA_WRITE;
    cmd.quiet = 0;
    cmd.timeout = 80*60;

	gui_flicker_led (LED_CD, 0, 1);

    io_error = ioctl (sdd->fd, CDROM_SEND_PACKET, &cmd);
    DEBUG_LOG ("SCSIDEV: error: %d, stat: %d\n", io_error, cmd.stat);
    if (io_error != 0) {
	DEBUG_LOG ("SCSIDEV: errno: %d, %s\n", errno, strerror (errno));
	return 0;
    }
    return cmd_data;
}
static int spti_read (struct dev_info_ioctl *ciw, int unitnum, uae_u8 *data, int sector, int sectorsize)
{
	uae_u8 cmd[12] = { 0xbe, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 };
	int tlen = sectorsize;

	if (sectorsize == 2048 || sectorsize == 2336 || sectorsize == 2328) {
		cmd[9] |= 1 << 4; // userdata
	} else if (sectorsize >= 2352) {
		cmd[9] |= 1 << 4; // userdata
		cmd[9] |= 1 << 3; // EDC&ECC
		cmd[9] |= 1 << 7; // sync
		cmd[9] |= 3 << 5; // header code
		if (sectorsize > 2352) {
			cmd[10] |= 1; // RAW P-W
		}
		if (sectorsize > 2352 + SUB_CHANNEL_SIZE) {
			cmd[9] |= 0x2 << 1; // C2
		}
	}
	cmd[3] = (uae_u8)(sector >> 16);
	cmd[4] = (uae_u8)(sector >> 8);
	cmd[5] = (uae_u8)(sector >> 0);
	if (unitnum >= 0)
		gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
	int len = sizeof cmd;
	return do_raw_scsi (ciw, unitnum,  cmd, len, data, tlen);
}
Exemple #3
0
static uae_u8 cdtvcr_battram_read (int addr)
{
	uae_u8 v;
	int offset;
	offset = addr & CDTVCR_RAM_MASK;
	if (offset >= CDTVCR_RAM_SIZE)
		return 0;
	gui_flicker_led (LED_MD, 0, 1);
	v = cdtvcr_ram[offset];
	return v;
}
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 (L"SCSI, H=%X:%d:%d:%d:%d: ", 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);
	}
	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 (L"SCSI ERROR, H=%X:%d:%d:%d:%d: ", di->handle, di->bus, di->path, di->target, di->lun);
		write_log (L"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[0] == 0 || swb->SenseBuf[0] == 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;
}
Exemple #5
0
static int execscsicmd (int unitnum, const uae_u8 *data, int len, uae_u8 *inbuf,
			int inlen)
{
    int sactual = 0;
    struct scsidevdata *sdd = &drives[unitnum];
    SCSI *scgp              = sdd->scgp;
    struct scg_cmd *scmd;

    scmd = scgp->scmd;

    DEBUG_LOG ("SCSIDEV: execscicmd data=%08lx len=%d, inbuf=%08lx"\
	       " inlen=%d\n", data, len, inbuf, inlen);

    uae_sem_wait (&scgp_sem);
    memset (scmd, 0, sizeof (*scmd));

    scmd->timeout   = 80 * 60;
    if (inbuf) {
	scmd->addr      = (caddr_t) inbuf;
	scmd->size      = inlen;
	scmd->flags     = SCG_RECV_DATA;
	memset (inbuf, 0, inlen);
    } else {
	scmd->flags     = SCG_DISRE_ENA;
    }

    scmd->cdb_len   = len;
    memcpy (&scmd->cdb, data, len);
    scmd->target    = sdd->target;
    scmd->sense_len = -1;
    scmd->sense_count = 0;
    *(uae_u8 *)&scmd->scb = 0;

    scg_settarget (scgp, sdd->bus, sdd->target, sdd->lun);
    scgp->cmdname    = "???";
    scgp->curcmdname = "???";

    DEBUG_LOG ("SCSIDEV: sending command: 0x%2x\n", scmd->cdb.g0_cdb.cmd);

	gui_flicker_led (LED_CD, 0, 1);

    scg_cmd (scgp);

    uae_sem_post (&scgp_sem);

    DEBUG_LOG ("SCSIDEV: result: %d %d\n", scmd->error, scmd->ux_errno);

    return scmd->size;
}
Exemple #6
0
static void cdtvcr_battram_write (int addr, int v)
{
	struct zfile *f;
	int offset = addr & CDTVCR_RAM_MASK;

	if (offset >= CDTVCR_RAM_SIZE)
		return;
	gui_flicker_led (LED_MD, 0, 2);
	if (cdtvcr_ram[offset] == v)
		return;
	cdtvcr_ram[offset] = v;
	f = zfile_fopen (currprefs.flashfile, _T("rb+"), ZFD_NORMAL);
	if (!f)
		return;
	zfile_fseek (f, offset, SEEK_SET);
	zfile_fwrite (cdtvcr_ram + offset, 1, 1, f);
	zfile_fclose (f);
}
Exemple #7
0
int32_t scsiesp_req_enqueue(SCSIRequest *req)
{
	struct scsi_data *sd = (struct scsi_data*)req->dev->handle;

	if (sd->device_type == UAEDEV_CD)
		gui_flicker_led (LED_CD, sd->id, 1);

	sd->data_len = 0;
	scsi_start_transfer(sd);
	scsi_emulate_analyze(sd);
#if 0
	write_log (_T("%02x.%02x.%02x.%02x.%02x.%02x\n"), sd->cmd[0], sd->cmd[1], sd->cmd[2], sd->cmd[3], sd->cmd[4], sd->cmd[5]);
#endif
	if (sd->direction <= 0)
		scsi_emulate_cmd(sd);
	if (sd->direction == 0)
		return 1;
	if (sd->direction > 0)
		return -sd->data_len;
	return sd->data_len;
}
Exemple #8
0
static int execscsicmd_direct (int unitnum, uaecptr acmd)
{
    int sactual = 0;
    struct scsidevdata *sdd = &drives[unitnum];
    SCSI *scgp              = sdd->scgp;
    struct scg_cmd *scmd    = scgp->scmd;

    uaecptr scsi_data      = get_long (acmd + 0);
    uae_u32 scsi_len       = get_long (acmd + 4);
    uaecptr scsi_cmd       = get_long (acmd + 12);
    int scsi_cmd_len       = get_word (acmd + 16);
    int scsi_cmd_len_orig  = scsi_cmd_len;
    uae_u8  scsi_flags     = get_byte (acmd + 20);
    uaecptr scsi_sense     = get_long (acmd + 22);
    uae_u16 scsi_sense_len = get_word (acmd + 26);
    int io_error           = 0;
    int parm;
    addrbank *bank_data    = &get_mem_bank (scsi_data);
    addrbank *bank_cmd	   = &get_mem_bank (scsi_cmd);
    uae_u8   *scsi_datap;
    uae_u8   *scsi_datap_org;

    DEBUG_LOG ("SCSIDEV: unit=%d: execscsicmd_direct\n", unitnum);

    /* do transfer directly to and from Amiga memory */
    if (!bank_data || !bank_data->check (scsi_data, scsi_len))
	return -5; /* IOERR_BADADDRESS */

    uae_sem_wait (&scgp_sem);

    memset (scmd, 0, sizeof (*scmd));
    /* the Amiga does not tell us how long the timeout shall be, so make it
     * _very_ long (specified in seconds) */
    scmd->timeout   = 80 * 60;
    scsi_datap      = scsi_datap_org = scsi_len
		      ? bank_data->xlateaddr (scsi_data) : 0;
    scmd->size      = scsi_len;
    scmd->flags     = (scsi_flags & 1) ? SCG_RECV_DATA : SCG_DISRE_ENA;
    memcpy (&scmd->cdb, bank_cmd->xlateaddr (scsi_cmd), scsi_cmd_len);
    scmd->target    = sdd->target;
    scmd->sense_len = (scsi_flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
		      (scsi_flags & 2) ? scsi_sense_len : /* SCSIF_AUTOSENSE */
		      -1;
    scmd->sense_count = 0;
    *(uae_u8 *)&scmd->scb = 0;
    if (sdd->isatapi)
	scsi_atapi_fixup_pre (scmd->cdb.cmd_cdb, &scsi_cmd_len, &scsi_datap,
			      &scsi_len, &parm);
    scmd->addr      = (caddr_t)scsi_datap;
    scmd->cdb_len   = scsi_cmd_len;

    scg_settarget (scgp, sdd->bus, sdd->target, sdd->lun);
    scgp->cmdname    = "???";
    scgp->curcmdname = "???";

    DEBUG_LOG ("SCSIDEV: sending command: 0x%2x\n", scmd->cdb.g0_cdb.cmd);

    scg_cmd (scgp);

    DEBUG_LOG ("SCSIDEV: result: %d %d %s\n", scmd->error, scmd->ux_errno,\
	       scgp->errstr);

	gui_flicker_led (LED_CD, 0, 1);

    put_word (acmd + 18, scmd->error == SCG_FATAL
					? 0 : scsi_cmd_len); /* fake scsi_CmdActual */
    put_byte (acmd + 21, *(uae_u8 *)&scmd->scb);	     /* scsi_Status */

    if (*(uae_u8 *)&scmd->scb) {
	io_error = 45; /* HFERR_BadStatus */
	/* copy sense? */
	for (sactual = 0;
	     scsi_sense && sactual < scsi_sense_len && sactual < scmd->sense_count;
	     sactual++) {
	     put_byte (scsi_sense + sactual, scmd->u_sense.cmd_sense[sactual]);
	}
	put_long (acmd + 8, 0); /* scsi_Actual */
    } else {
	int i;
	for (i = 0; i < scsi_sense_len; i++)
	    put_byte (scsi_sense + i, 0);
	sactual = 0;
	if (scmd->error != SCG_NO_ERROR || scmd->ux_errno != 0) {
	    /* We might have been limited by the hosts DMA limits,
	       which is usually indicated by ENOMEM */
	    if (scsi_len > (unsigned int)sdd->max_dma && scmd->ux_errno == ENOMEM)
		io_error = (uae_u8)-4; /* IOERR_BADLENGTH */
	    else {
		io_error = 20; /* io_Error, but not specified */
		put_long (acmd + 8, 0); /* scsi_Actual */
	    }
	} else {
	    scsi_len = scmd->size;
	    if (sdd->isatapi)
		scsi_atapi_fixup_post (scmd->cdb.cmd_cdb, scsi_cmd_len,
				       scsi_datap_org, scsi_datap,
				       &scsi_len, parm);
	    io_error = 0;
	    put_long (acmd + 8, scsi_len); /* scsi_Actual */
	}
    }
    put_word (acmd + 28, sactual);

    uae_sem_post (&scgp_sem);

    if (scsi_datap != scsi_datap_org)
	free (scsi_datap);

    return io_error;
}
static int execscsicmd_direct_ioctl (int unitnum, struct amigascsi* ascsi)
{
    struct scsidevdata *sdd;
    struct cdrom_generic_command cmd;
    struct request_sense sense;
    uaecptr acmd = VALUE_TO_PTR(ascsi);

    uaecptr scsi_data         = get_long (acmd + 0);
    uae_u32 scsi_len          = get_long (acmd + 4);
    uaecptr scsi_cmd          = get_long (acmd + 12);
    int     scsi_cmd_len      = get_word (acmd + 16);
    uae_u8  scsi_flags        = get_byte (acmd + 20);
    uae_u8  scsi_status       = get_byte (acmd + 21);
    uaecptr scsi_sense        = get_long (acmd + 22);
    uae_u16 scsi_sense_len    = get_word (acmd + 26);

    int io_error;
    unsigned int senselen;
    int parm, i;

    addrbank *bank_data    = &get_mem_bank (scsi_data);
    addrbank *bank_cmd	   = &get_mem_bank (scsi_cmd);
    addrbank *bank_sense   = &get_mem_bank (scsi_sense);

    uae_u8   *scsi_datap;
    uae_u8   *scsi_datap_org;

    DEBUG_LOG ("SCSIDEV: unit = %d: execscsicmd_direct_ioctl\n", unitnum);
    DEBUG_LOG ("SCSIDEV: scsi_len = %d, scsi_cmd_len = %d, scsi_sense_len = %d, scsi_flags = %x\n",
	       scsi_len, scsi_cmd_len, scsi_sense_len, scsi_flags);

    if (unitnum >= total_drives) {
	DEBUG_LOG ("SCSIDEV: illegal unit %d >= total_drives %d.\n", unitnum, total_drives);
	return -1; /* TODO: better error code */
    }
    sdd = &drives[unitnum];

    /* do transfer directly to and from Amiga memory */
    if (!bank_data || !bank_data->check (scsi_data, scsi_len)) {
	DEBUG_LOG ("SCSIDEV: illegal Amiga memory buffer\n");
	return -5; /* IOERR_BADADDRESS */
    }

    if (scsi_cmd_len > CDROM_PACKET_SIZE) {
	DEBUG_LOG ("SCSIDEV: scsi_cmd_len too large (%d)\n", scsi_cmd_len);
	return -5; /* TODO: better code */
    }

    scsi_datap = scsi_datap_org = (scsi_len ? bank_data->xlateaddr (scsi_data) : 0);

    memcpy (cmd.cmd, bank_cmd->xlateaddr (scsi_cmd), scsi_cmd_len);
    cmd.buffer = scsi_datap;
    cmd.buflen = scsi_len;
    cmd.stat = scsi_status;
    if (sdd->isatapi) {
	scsi_atapi_fixup_pre (cmd.cmd, &scsi_cmd_len, &scsi_datap,
			      &scsi_len, &parm);
    }
    senselen = (scsi_flags & 4) ? 4 : /* SCSIF_OLDAUTOSENSE */
		      (scsi_flags & 2) ? scsi_sense_len : /* SCSIF_AUTOSENSE */
		      0;
    cmd.sense = senselen > 0 ? &sense : 0;
    cmd.data_direction = (scsi_flags & 1) ? CGC_DATA_READ : CGC_DATA_WRITE;
    cmd.quiet = 0;
    cmd.timeout = 80*60;

        gui_flicker_led (LED_CD, 0, 1);

    io_error = ioctl (sdd->fd, CDROM_SEND_PACKET, &cmd);

    DEBUG_LOG ("SCSIDEV: error: %d, stat: %d\n", io_error, cmd.stat);

    if (cmd.stat != 0) {
	unsigned int n;

	io_error = 45;  /* HFERR_BadStatus */
	put_byte (acmd + 8, 0);
	put_byte (acmd + 18, 0 /*scsi_cmd_len */);
	put_byte (acmd + 21, cmd.stat);
	DEBUG_LOG ("SCSIDEV: bad status\n");
	n = cmd.sense ? cmd.sense->add_sense_len + 7 : 0;
	if (senselen > n) {
	    if (scsi_sense)
		memset (bank_sense->xlateaddr (scsi_sense), 0, senselen);
	    senselen = n;
	}
	DEBUG_LOG ("SCSIDEV: senselen = %d\n", senselen);
	if (scsi_sense && cmd.sense && senselen > 0) {
	    memcpy (bank_sense->xlateaddr (scsi_sense), cmd.sense, senselen);
	}
	put_byte (acmd + 28, senselen);
    } else {
	put_byte (acmd + 28, 0);
	if (scsi_sense && senselen > 0) {
	    memset (bank_sense->xlateaddr (scsi_sense), 0, senselen);
	}
	if (io_error == 0) {
	    if (sdd->isatapi) {
		scsi_atapi_fixup_post (cmd.cmd, scsi_cmd_len,
				       scsi_datap_org, scsi_datap,
				       &scsi_len, parm);
	    }
	    put_long (acmd + 8,  scsi_len);
	    put_word (acmd + 18, scsi_cmd_len);
	    put_byte (acmd + 21, cmd.stat);
	    io_error = 0;
	} else {
	    DEBUG_LOG ("SCSIDEV: errno: %d, %s\n", errno, strerror (errno));
	    put_long (acmd + 8,  0);
	    put_word (acmd + 18, 0);
	    put_byte (acmd + 21, cmd.stat);
	    io_error = 20; /* TODO: Map errors */
	}
    }
    if (scsi_datap != scsi_datap_org)
	xfree (scsi_datap);
    return io_error;
}
Exemple #10
0
static void *cdda_play_func (void *v)
{
	int cdda_pos;
	int num_sectors = CDDA_BUFFERS;
	int quit = 0;
	int bufnum;
	int bufon[2];
	int oldplay;
	int idleframes;
	bool foundsub;
	struct cdunit *cdu = (struct cdunit*)v;

	while (cdu->cdda_play == 0)
		Sleep (10);
	oldplay = -1;

	bufon[0] = bufon[1] = 0;
	bufnum = 0;

	cda_audio *cda = new cda_audio (num_sectors);

	while (cdu->cdda_play > 0) {

		if (oldplay != cdu->cdda_play) {
			struct cdtoc *t;
			int sector, diff;
			struct _timeb tb1, tb2;

			idleframes = 0;
			foundsub = false;
			_ftime (&tb1);
			cdda_pos = cdu->cdda_start;
			oldplay = cdu->cdda_play;
			sector = cdu->cd_last_pos = cdda_pos;
			t = findtoc (cdu, &sector);
			if (!t) {
				write_log (_T("IMAGE CDDA: illegal sector number %d\n"), cdu->cdda_start);
				setstate (cdu, AUDIO_STATUS_PLAY_ERROR);
			} else {
				write_log (_T("IMAGE CDDA: playing from %d to %d, track %d ('%s', offset %lld, secoffset %d)\n"),
					cdu->cdda_start, cdu->cdda_end, t->track, t->fname, t->offset, sector);
				// do this even if audio is not compressed, t->handle also could be
				// compressed and we want to unpack it in background too
				while (cdimage_unpack_active == 1)
					Sleep (10);
				cdimage_unpack_active = 0;
				write_comm_pipe_u32 (&unpack_pipe, cdu - &cdunits[0], 0);
				write_comm_pipe_u32 (&unpack_pipe, t - &cdu->toc[0], 1);
				while (cdimage_unpack_active == 0)
					Sleep (10);
			}
			idleframes = cdu->cdda_delay_frames;
			while (cdu->cdda_paused && cdu->cdda_play > 0) {
				Sleep (10);
				idleframes = -1;
			}

			if (cdu->cdda_scan == 0) {
				// find possible P-subchannel=1 and fudge starting point so that
				// buggy CD32/CDTV software CD+G handling does not miss any frames
				bool seenindex = false;
				for (sector = cdda_pos - 200; sector < cdda_pos; sector++) {
					int sec = sector;
					t = findtoc (cdu, &sec);
					if (t) {
						uae_u8 subbuf[SUB_CHANNEL_SIZE];
						getsub_deinterleaved (subbuf, cdu, t, sector);
						if (seenindex) {
							for (int i = 2 * SUB_ENTRY_SIZE; i < SUB_CHANNEL_SIZE; i++) {
								if (subbuf[i]) { // non-zero R-W subchannels
									int diff = cdda_pos - sector + 2;
									write_log (_T("-> CD+G start pos fudge -> %d (%d)\n"), sector, -diff);
									idleframes -= diff;
									cdda_pos = sector;
									break;
								}
							}
						} else if (subbuf[0] == 0xff) { // P == 1?
							seenindex = true;
						}
					}
				}
			}
			cdda_pos -= idleframes;

			_ftime (&tb2);
			diff = (tb2.time * (uae_s64)1000 + tb2.millitm) - (tb1.time * (uae_s64)1000 + tb1.millitm);
			diff -= cdu->cdda_delay;
			if (idleframes >= 0 && diff < 0 && cdu->cdda_play > 0)
				Sleep (-diff);
			setstate (cdu, AUDIO_STATUS_IN_PROGRESS);
		}

		cda->wait(bufnum);
		bufon[bufnum] = 0;
		if (!cdu->cdda_play)
			goto end;

		if (idleframes <= 0 && cdda_pos >= cdu->cdda_start && !isaudiotrack (&cdu->di.toc, cdda_pos)) {
			setstate (cdu, AUDIO_STATUS_PLAY_ERROR);
			write_log (_T("IMAGE CDDA: attempted to play data track %d\n"), cdda_pos);
			goto end; // data track?
		}

		if ((cdda_pos < cdu->cdda_end || cdu->cdda_end == 0xffffffff) && !cdu->cdda_paused && cdu->cdda_play > 0) {
			struct cdtoc *t;
			int sector, cnt;
			int dofinish = 0;

			gui_flicker_led (LED_CD, cdu->di.unitnum - 1, LED_CD_AUDIO);

			memset (cda->buffers[bufnum], 0, num_sectors * 2352);

			for (cnt = 0; cnt < num_sectors; cnt++) {
				uae_u8 *dst = cda->buffers[bufnum] + cnt * 2352;
				uae_u8 subbuf[SUB_CHANNEL_SIZE];
				sector = cdda_pos;

				memset (subbuf, 0, SUB_CHANNEL_SIZE);

				t = findtoc (cdu, &sector);
				if (t) {
					if (!(t->ctrl & 4)) {
						if (t->enctype == ENC_CHD) {
							do_read (cdu, t, dst, sector, 0, t->size);
							for (int i = 0; i < 2352; i+=2) {
								uae_u8 p;
								p = dst[i + 0];
								dst[i + 0] = dst[i + 1];
								dst[i +1] = p;
							}
						} else if (t->handle) {
							int totalsize = t->size + t->skipsize;
							if ((t->enctype == AUDENC_MP3 || t->enctype == AUDENC_FLAC) && t->data) {
								if (t->filesize >= sector * totalsize + t->offset + t->size)
									memcpy (dst, t->data + sector * totalsize + t->offset, t->size);
							} else if (t->enctype == AUDENC_PCM) {
								if (sector * totalsize + t->offset + totalsize < t->filesize) {
									zfile_fseek (t->handle, (uae_u64)sector * totalsize + t->offset, SEEK_SET);
									zfile_fread (dst, t->size, 1, t->handle);
								}
							}
						}
					}
					getsub_deinterleaved (subbuf, cdu, t, cdda_pos);
				}

				if (idleframes > 0) {
					idleframes--;
					memset (dst, 0, 2352);
					memset (subbuf, 0, SUB_CHANNEL_SIZE);
				}

				if (cdda_pos < cdu->cdda_start && cdu->cdda_scan == 0)
					memset (dst, 0, 2352);

				dosub (cdu, subbuf);

				if (cdu->cdda_scan) {
					cdda_pos += cdu->cdda_scan;
					if (cdda_pos < 0)
						cdda_pos = 0;
				} else  {
					cdda_pos++;
				}

				if (cdda_pos - num_sectors < cdu->cdda_end && cdda_pos >= cdu->cdda_end)
					dofinish = 1;

			}
	
			if (idleframes <= 0)
				cdu->cd_last_pos = cdda_pos;

			bufon[bufnum] = 1;
			cda->setvolume (currprefs.sound_volume_cd >= 0 ? currprefs.sound_volume_cd : currprefs.sound_volume, cdu->cdda_volume[0], cdu->cdda_volume[1]);
			if (!cda->play (bufnum)) {
				setstate (cdu, AUDIO_STATUS_PLAY_ERROR);
				goto end;
			}

			if (dofinish) {
				setstate (cdu, AUDIO_STATUS_PLAY_COMPLETE);
				cdu->cdda_play = -1;
				cdda_pos = cdu->cdda_end + 1;
			}

		}

		if (bufon[0] == 0 && bufon[1] == 0) {
			while (cdu->cdda_paused && cdu->cdda_play == oldplay)
				Sleep (10);
		}

		bufnum = 1 - bufnum;
	}

end:
	cda->wait (0);
	cda->wait (1);

	while (cdimage_unpack_active == 1)
		Sleep (10);

	delete cda;

	cdu->cdda_play = 0;
	write_log (_T("IMAGE CDDA: thread killed\n"));
	return NULL;
}
static int ioctl_command_readwrite (int unitnum, int sector, int size, int write, uae_u8 *data)
{
	struct dev_info_ioctl *ciw = unitisopen (unitnum);
	if (!ciw)
		return 0;

	if (ciw->usesptiread)
		return ioctl_command_rawread (unitnum, data, sector, size, 2048, 0);

	cdda_stop (ciw);

	DWORD dtotal;
	int cnt = 3;
	uae_u8 *p = ciw->tempbuffer;
	int blocksize = ciw->di.bytespersector;

	if (!open_createfile (ciw, 0))
		return 0;
	ciw->cd_last_pos = sector;
	while (cnt-- > 0) {
		LARGE_INTEGER offset;
		gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
		seterrormode (ciw);
		offset.QuadPart = (uae_u64)sector * ciw->di.bytespersector;
		if (SetFilePointer (ciw->h, offset.LowPart, &offset.HighPart, FILE_BEGIN) == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) {
			reseterrormode (ciw);
			if (win32_error (ciw, unitnum, _T("SetFilePointer")) < 0)
				continue;
			return 0;
		}
		reseterrormode (ciw);
		break;
	}
	while (size-- > 0) {
		gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
		seterrormode (ciw);
		if (write) {
			if (data) {
				memcpy (p, data, blocksize);
				data += blocksize;
			}
			if (!WriteFile (ciw->h, p, blocksize, &dtotal, 0)) {
				int err;
				reseterrormode (ciw);
				err = win32_error (ciw, unitnum, _T("WriteFile"));
				if (err < 0)
					continue;
				if (err == ERROR_WRITE_PROTECT)
					return -1;
				return 0;
			}
		} else {
			dtotal = 0;
			if (!ReadFile (ciw->h, p, blocksize, &dtotal, 0)) {
				reseterrormode (ciw);
				if (win32_error (ciw, unitnum, _T("ReadFile")) < 0)
					continue;
				return 0;
			}
			if (dtotal == 0) {
				static int reported;
				/* ESS Mega (CDTV) "fake" data area returns zero bytes and no error.. */
				spti_read (ciw, unitnum, data, sector, 2048);
				if (reported++ < 100)
					write_log (_T("IOCTL unit %d, sector %d: ReadFile()==0. SPTI=%d\n"), unitnum, sector, GetLastError ());
				return 1;
			}
			if (data) {
				memcpy (data, p, blocksize);
				data += blocksize;
			}
		}
		reseterrormode (ciw);
		gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
	}
	return 1;
}
static int ioctl_command_rawread (int unitnum, uae_u8 *data, int sector, int size, int sectorsize, uae_u32 extra)
{
	struct dev_info_ioctl *ciw = unitisopen (unitnum);
	if (!ciw)
		return 0;

	uae_u8 *p = ciw->tempbuffer;
	int ret = 0;

	if (log_scsi)
		write_log (_T("IOCTL rawread unit=%d sector=%d blocksize=%d\n"), unitnum, sector, sectorsize);
	cdda_stop (ciw);
	gui_flicker_led (LED_CD, unitnum, LED_CD_ACTIVE);
	if (sectorsize > 0) {
		if (sectorsize != 2336 && sectorsize != 2352 && sectorsize != 2048 &&
			sectorsize != 2336 + 96 && sectorsize != 2352 + 96 && sectorsize != 2048 + 96)
			return 0;
		while (size-- > 0) {
			if (!read_block (ciw, unitnum, data, sector, 1, sectorsize))
				break;
			ciw->cd_last_pos = sector;
			data += sectorsize;
			ret += sectorsize;
			sector++;
		}
	} else {
		uae_u8 sectortype = extra >> 16;
		uae_u8 cmd9 = extra >> 8;
		int sync = (cmd9 >> 7) & 1;
		int headercodes = (cmd9 >> 5) & 3;
		int userdata = (cmd9 >> 4) & 1;
		int edcecc = (cmd9 >> 3) & 1;
		int errorfield = (cmd9 >> 1) & 3;
		uae_u8 subs = extra & 7;
		if (subs != 0 && subs != 1 && subs != 2 && subs != 4)
			return -1;
		if (errorfield >= 3)
			return -1;
		uae_u8 *d = data;

		if (isaudiotrack (&ciw->di.toc, sector)) {

			if (sectortype != 0 && sectortype != 1)
				return -2;

			for (int i = 0; i < size; i++) {
				uae_u8 *odata = data;
				int blocksize = errorfield == 0 ? 2352 : (errorfield == 1 ? 2352 + 294 : 2352 + 296);
				int readblocksize = errorfield == 0 ? 2352 : 2352 + 296;

				if (!read_block (ciw, unitnum, NULL, sector, 1, readblocksize)) {
					reseterrormode (ciw);
					return ret;
				}
				ciw->cd_last_pos = sector;

				if (subs == 0) {
					memcpy (data, p, blocksize);
					data += blocksize;
				} else if (subs == 4) { // all, de-interleaved
					memcpy (data, p, blocksize);
					data += blocksize;
					sub_to_deinterleaved (p + readblocksize, data);
					data += SUB_CHANNEL_SIZE;
				} else if (subs == 2) { // q-only
					memcpy (data, p, blocksize);
					data += blocksize;
					uae_u8 subdata[SUB_CHANNEL_SIZE];
					sub_to_deinterleaved (p + readblocksize, subdata);
					memcpy (data, subdata + SUB_ENTRY_SIZE, SUB_ENTRY_SIZE);
					p += SUB_ENTRY_SIZE;
				} else if (subs == 1) { // all, interleaved
					memcpy (data, p, blocksize);
					memcpy (data + blocksize, p + readblocksize, SUB_CHANNEL_SIZE);
					data += blocksize + SUB_CHANNEL_SIZE;
				}
				ret += data - odata;
				sector++;
			}
		}


	}
	return ret;
}
static void *cdda_play (void *v)
{
	struct dev_info_ioctl *ciw = (struct dev_info_ioctl*)v;
	int cdda_pos;
	int num_sectors = CDDA_BUFFERS;
	int bufnum;
	int buffered;
	int bufon[2];
	int i;
	int oldplay;
	int idleframes;
	int muteframes;
	int readblocksize = 2352 + 96;

	while (ciw->cdda_play == 0)
		Sleep (10);
	oldplay = -1;

	bufon[0] = bufon[1] = 0;
	bufnum = 0;
	buffered = 0;

	cda_audio *cda = new cda_audio (num_sectors);

	while (ciw->cdda_play > 0) {

		cda->wait(bufnum);
		if (ciw->cdda_play <= 0)
			goto end;
		bufon[bufnum] = 0;

		if (oldplay != ciw->cdda_play) {
			idleframes = 0;
			muteframes = 0;
			bool seensub = false;
			struct _timeb tb1, tb2;
			_ftime (&tb1);
			cdda_pos = ciw->cdda_start;
			ciw->cd_last_pos = cdda_pos;
			oldplay = ciw->cdda_play;
			write_log (_T("IOCTL%s CDDA: playing from %d to %d\n"),
				ciw->usesptiread ? _T("(SPTI)") : _T(""), ciw->cdda_start, ciw->cdda_end);
			ciw->subcodevalid = false;
			idleframes = ciw->cdda_delay_frames;
			while (ciw->cdda_paused && ciw->cdda_play > 0) {
				Sleep (10);
				idleframes = -1;
			}
			// force spin up
			if (isaudiotrack (&ciw->di.toc, cdda_pos))
				read_block (ciw, -1, cda->buffers[bufnum], cdda_pos, num_sectors, readblocksize);
			if (!isaudiotrack (&ciw->di.toc, cdda_pos - 150))
				muteframes = 75;

			if (ciw->cdda_scan == 0) {
				// find possible P-subchannel=1 and fudge starting point so that
				// buggy CD32/CDTV software CD+G handling does not miss any frames
				bool seenindex = false;
				for (int sector = cdda_pos - 200; sector < cdda_pos; sector++) {
					uae_u8 *dst = cda->buffers[bufnum];
					if (sector >= 0 && isaudiotrack (&ciw->di.toc, sector) && read_block (ciw, -1, dst, sector, 1, readblocksize)) {
						uae_u8 subbuf[SUB_CHANNEL_SIZE];
						sub_deinterleave (dst + 2352, subbuf);
						if (seenindex) {
							for (int i = 2 * SUB_ENTRY_SIZE; i < SUB_CHANNEL_SIZE; i++) {
								if (subbuf[i]) { // non-zero R-W subchannels?
									int diff = cdda_pos - sector + 2;
									write_log (_T("-> CD+G start pos fudge -> %d (%d)\n"), sector, -diff);
									idleframes -= diff;
									cdda_pos = sector;
									seensub = true;
									break;
								}
							}
						} else if (subbuf[0] == 0xff) { // P == 1?
							seenindex = true;
						}
					}
				}
			}
			cdda_pos -= idleframes;

			_ftime (&tb2);
			int diff = (tb2.time * (uae_s64)1000 + tb2.millitm) - (tb1.time * (uae_s64)1000 + tb1.millitm);
			diff -= ciw->cdda_delay;
			if (idleframes >= 0 && diff < 0 && ciw->cdda_play > 0)
				Sleep (-diff);
			if (diff > 0 && !seensub) {
				int ch = diff / 7 + 25;
				if (ch > idleframes)
					ch = idleframes;
				idleframes -= ch;
				cdda_pos += ch;
			}

			setstate (ciw, AUDIO_STATUS_IN_PROGRESS);
		}

		if ((cdda_pos < ciw->cdda_end || ciw->cdda_end == 0xffffffff) && !ciw->cdda_paused && ciw->cdda_play) {

			if (idleframes <= 0 && cdda_pos >= ciw->cdda_start && !isaudiotrack (&ciw->di.toc, cdda_pos)) {
				setstate (ciw, AUDIO_STATUS_PLAY_ERROR);
				write_log (_T("IOCTL: attempted to play data track %d\n"), cdda_pos);
				goto end; // data track?
			}

			gui_flicker_led (LED_CD, ciw->di.unitnum - 1, LED_CD_AUDIO);

			uae_sem_wait (&ciw->sub_sem);

			ciw->subcodevalid = false;
			memset (ciw->subcode, 0, sizeof ciw->subcode);
			memset (cda->buffers[bufnum], 0, num_sectors * readblocksize);

			if (cdda_pos >= 0) {
				if (read_block (ciw, -1, cda->buffers[bufnum], cdda_pos, num_sectors, readblocksize)) {
					for (i = 0; i < num_sectors; i++) {
						memcpy (ciw->subcode + i * SUB_CHANNEL_SIZE, cda->buffers[bufnum] + readblocksize * i + 2352, SUB_CHANNEL_SIZE);
					}
					for (i = 1; i < num_sectors; i++) {
						memmove (cda->buffers[bufnum] + 2352 * i, cda->buffers[bufnum] + readblocksize * i, 2352);
					}
					ciw->subcodevalid = true;
				}
			}

			for (i = 0; i < num_sectors; i++) {
				if (muteframes > 0) {
					memset (cda->buffers[bufnum] + 2352 * i, 0, 2352);
					muteframes--;
				}
				if (idleframes > 0) {
					idleframes--;
					memset (cda->buffers[bufnum] + 2352 * i, 0, 2352);
					memset (ciw->subcode + i * SUB_CHANNEL_SIZE, 0, SUB_CHANNEL_SIZE);
				} else if (cdda_pos < ciw->cdda_start && ciw->cdda_scan == 0) {
					memset (cda->buffers[bufnum] + 2352 * i, 0, 2352);
				}
			}
			if (idleframes > 0)
				ciw->subcodevalid = false;

			if (ciw->cdda_subfunc)
				ciw->cdda_subfunc (ciw->subcode, num_sectors); 

			uae_sem_post (&ciw->sub_sem);

			if (ciw->subcodevalid) {
				uae_sem_wait (&ciw->sub_sem2);
				memcpy (ciw->subcodebuf, ciw->subcode + (num_sectors - 1) * SUB_CHANNEL_SIZE, SUB_CHANNEL_SIZE);
				uae_sem_post (&ciw->sub_sem2);
			}

			bufon[bufnum] = 1;
			cda->setvolume (currprefs.sound_volume_cd >= 0 ? currprefs.sound_volume_cd : currprefs.sound_volume, ciw->cdda_volume[0], ciw->cdda_volume[1]);
			if (!cda->play (bufnum)) {
				setstate (ciw, AUDIO_STATUS_PLAY_ERROR);
				goto end; // data track?
			}

			if (ciw->cdda_scan) {
				cdda_pos += ciw->cdda_scan * num_sectors;
				if (cdda_pos < 0)
					cdda_pos = 0;
			} else  {
				if (cdda_pos < 0 && cdda_pos + num_sectors >= 0)
					cdda_pos = 0;
				else
					cdda_pos += num_sectors;
			}

			if (idleframes <= 0) {
				if (cdda_pos - num_sectors < ciw->cdda_end && cdda_pos >= ciw->cdda_end) {
					setstate (ciw, AUDIO_STATUS_PLAY_COMPLETE);
					ciw->cdda_play_finished = 1;
					ciw->cdda_play = -1;
					cdda_pos = ciw->cdda_end;
				}
				ciw->cd_last_pos = cdda_pos;
			}
		}

		while (ciw->cdda_paused && ciw->cdda_play == oldplay)
			Sleep (10);

		bufnum = 1 - bufnum;

	}

end:
	ciw->subcodevalid = false;
	delete cda;

	ciw->cdda_play = 0;
	write_log (_T("IOCTL CDDA: thread killed\n"));
	return NULL;
}