Files
86Box/src/cdrom/cdrom.c

3858 lines
104 KiB
C
Raw Normal View History

/*
* 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.
*
* Implementation of the CD-ROM drive with SCSI(-like)
* commands, for both ATAPI and SCSI usage.
*
* Version: @(#)cdrom.c 1.0.5 2017/09/19
*
* Author: Miran Grca, <mgrca8@gmail.com>
* Copyright 2016,2017 Miran Grca.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "../86box.h"
#include "../ibm.h"
#include "../piix.h"
#include "../scsi/scsi.h"
#include "../timer.h"
#include "../nvr.h"
#include "../hdd/hdd_ide_at.h"
#include "../win/plat_iodev.h"
#include "cdrom.h"
/* Bits of 'status' */
#define ERR_STAT 0x01
#define DRQ_STAT 0x08 /* Data request */
#define DSC_STAT 0x10
#define SERVICE_STAT 0x10
#define READY_STAT 0x40
#define BUSY_STAT 0x80
/* Bits of 'error' */
#define ABRT_ERR 0x04 /* Command aborted */
#define MCR_ERR 0x08 /* Media change request */
#define MODE_SELECT_PHASE_IDLE 0
#define MODE_SELECT_PHASE_HEADER 1
#define MODE_SELECT_PHASE_BLOCK_DESC 2
#define MODE_SELECT_PHASE_PAGE_HEADER 3
#define MODE_SELECT_PHASE_PAGE 4
cdrom_t cdrom[CDROM_NUM];
cdrom_drive_t cdrom_drives[CDROM_NUM];
uint8_t atapi_cdrom_drives[8] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
uint8_t scsi_cdrom_drives[16][8] = { { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF } };
#ifdef __MSC__
# pragma pack(push,1)
static struct
#else
static struct __attribute__((__packed__))
#endif
{
uint8_t opcode;
uint8_t polled;
uint8_t reserved2[2];
uint8_t class;
uint8_t reserved3[2];
uint16_t len;
uint8_t control;
} *gesn_cdb;
#ifdef __MSC__
# pragma pack(pop)
#endif
#ifdef __MSC__
# pragma pack(push,1)
static struct
#else
static struct __attribute__((__packed__))
#endif
{
uint16_t len;
uint8_t notification_class;
uint8_t supported_events;
} *gesn_event_header;
#ifdef __MSC__
# pragma pack(pop)
#endif
/* Table of all SCSI commands and their flags, needed for the new disc change / not ready handler. */
uint8_t cdrom_command_flags[0x100] =
{
IMPLEMENTED | CHECK_READY | NONDATA,
IMPLEMENTED | ALLOW_UA | NONDATA | SCSI_ONLY,
0,
IMPLEMENTED | ALLOW_UA,
0, 0, 0, 0,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY | NONDATA,
0, 0, 0, 0, 0, 0,
IMPLEMENTED | ALLOW_UA,
IMPLEMENTED | CHECK_READY | NONDATA | SCSI_ONLY,
0,
IMPLEMENTED,
0, 0, 0, 0,
IMPLEMENTED,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY,
0, 0, 0, 0, 0, 0,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY | NONDATA,
0, 0, 0,
IMPLEMENTED | CHECK_READY | NONDATA | SCSI_ONLY,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY, /* Read TOC - can get through UNIT_ATTENTION, per VIDE-CDD.SYS
NOTE: The ATAPI reference says otherwise, but I think this is a question of
interpreting things right - the UNIT ATTENTION condition we have here
is a tradition from not ready to ready, by definition the drive
eventually becomes ready, make the condition go away. */
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | ALLOW_UA,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY,
0,
IMPLEMENTED | ALLOW_UA,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED,
0, 0, 0, 0,
IMPLEMENTED,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY,
0, 0, 0, 0,
IMPLEMENTED | CHECK_READY,
0,
IMPLEMENTED | CHECK_READY | NONDATA | SCSI_ONLY,
0, 0, 0, 0,
IMPLEMENTED | CHECK_READY | ATAPI_ONLY,
0, 0, 0,
IMPLEMENTED | CHECK_READY | ATAPI_ONLY,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED,
IMPLEMENTED | CHECK_READY,
IMPLEMENTED | CHECK_READY,
0, 0,
IMPLEMENTED | CHECK_READY | SCSI_ONLY,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
IMPLEMENTED | CHECK_READY | SCSI_ONLY,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
IMPLEMENTED | SCSI_ONLY,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
uint64_t cdrom_mode_sense_page_flags[CDROM_NUM] = { (1LL << GPMODE_R_W_ERROR_PAGE) | (1LL << GPMODE_CDROM_PAGE) | (1LL << GPMODE_CDROM_AUDIO_PAGE) | (1LL << GPMODE_CAPABILITIES_PAGE) | (1LL << GPMODE_ALL_PAGES),
(1LL << GPMODE_R_W_ERROR_PAGE) | (1LL << GPMODE_CDROM_PAGE) | (1LL << GPMODE_CDROM_AUDIO_PAGE) | (1LL << GPMODE_CAPABILITIES_PAGE) | (1LL << GPMODE_ALL_PAGES),
(1LL << GPMODE_R_W_ERROR_PAGE) | (1LL << GPMODE_CDROM_PAGE) | (1LL << GPMODE_CDROM_AUDIO_PAGE) | (1LL << GPMODE_CAPABILITIES_PAGE) | (1LL << GPMODE_ALL_PAGES),
(1LL << GPMODE_R_W_ERROR_PAGE) | (1LL << GPMODE_CDROM_PAGE) | (1LL << GPMODE_CDROM_AUDIO_PAGE) | (1LL << GPMODE_CAPABILITIES_PAGE) | (1LL << GPMODE_ALL_PAGES) };
const uint8_t cdrom_mode_sense_pages_default[CDROM_NUM][0x40][0x40] =
{
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } }
};
uint8_t cdrom_mode_sense_pages_changeable[CDROM_NUM][0x40][0x40] =
{
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } }
};
uint8_t cdrom_mode_sense_pages_saved[CDROM_NUM][0x40][0x40] =
{
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } },
{ { 0, 0 },
{ GPMODE_R_W_ERROR_PAGE, 6, 0, 5, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CDROM_PAGE, 6, 0, 1, 0, 60, 0, 75 },
{ 0x8E, 0xE, 4, 0, 0, 0, 0, 75, 1, 0xFF, 2, 0xFF, 0, 0, 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ 0, 0 },
{ GPMODE_CAPABILITIES_PAGE, 0x12, 0, 0, 1, 0, 0, 0, 0x02, 0xC2, 0, 2, 0, 0, 0x02, 0xC2, 0, 0, 0, 0 } }
};
int cdrom_do_log = 0;
void cdrom_log(const char *format, ...)
{
#ifdef ENABLE_CDROM_LOG
if (cdrom_do_log)
{
va_list ap;
va_start(ap, format);
vprintf(format, ap);
va_end(ap);
fflush(stdout);
}
#endif
}
int cdrom_mode_select_terminate(uint8_t id, int force);
int find_cdrom_for_channel(uint8_t channel)
{
uint8_t i = 0;
for (i = 0; i < CDROM_NUM; i++)
{
if (((cdrom_drives[i].bus_type == CDROM_BUS_ATAPI_PIO_ONLY) || (cdrom_drives[i].bus_type == CDROM_BUS_ATAPI_PIO_AND_DMA)) && (cdrom_drives[i].ide_channel == channel))
{
return i;
}
}
return 0xff;
}
void cdrom_init(int id, int cdb_len_setting);
void build_atapi_cdrom_map()
{
uint8_t i = 0;
memset(atapi_cdrom_drives, 0xff, 8);
for (i = 0; i < 8; i++)
{
atapi_cdrom_drives[i] = find_cdrom_for_channel(i);
if (atapi_cdrom_drives[i] != 0xff)
{
cdrom_init(atapi_cdrom_drives[i], 12);
}
}
}
int find_cdrom_for_scsi_id(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t i = 0;
for (i = 0; i < CDROM_NUM; i++)
{
if ((cdrom_drives[i].bus_type == CDROM_BUS_SCSI) && (cdrom_drives[i].scsi_device_id == scsi_id) && (cdrom_drives[i].scsi_device_lun == scsi_lun))
{
return i;
}
}
return 0xff;
}
void build_scsi_cdrom_map()
{
uint8_t i = 0;
uint8_t j = 0;
for (i = 0; i < 16; i++)
{
memset(scsi_cdrom_drives[i], 0xff, 8);
}
for (i = 0; i < 16; i++)
{
for (j = 0; j < 8; j++)
{
scsi_cdrom_drives[i][j] = find_cdrom_for_scsi_id(i, j);
if (scsi_cdrom_drives[i][j] != 0xff)
{
cdrom_init(scsi_cdrom_drives[i][j], 12);
}
}
}
}
void cdrom_set_cdb_len(int id, int cdb_len)
{
cdrom[id].cdb_len = cdb_len;
}
void cdrom_reset_cdb_len(int id)
{
cdrom[id].cdb_len = cdrom[id].cdb_len_setting ? 16 : 12;
}
void cdrom_set_signature(int id)
{
if (id >= CDROM_NUM)
{
return;
}
cdrom[id].phase = 1;
cdrom[id].request_length = 0xEB14;
}
void cdrom_init(int id, int cdb_len_setting)
{
if (id >= CDROM_NUM)
{
return;
}
memset(&(cdrom[id]), 0, sizeof(cdrom_t));
cdrom[id].requested_blocks = 1;
if (cdb_len_setting <= 1)
{
cdrom[id].cdb_len_setting = cdb_len_setting;
}
cdrom_reset_cdb_len(id);
cdrom_mode_select_terminate(id, 1);
cdrom[id].cd_status = CD_STATUS_EMPTY;
cdrom[id].sense[0] = 0xf0;
cdrom[id].sense[7] = 10;
cdrom_drives[id].bus_mode = 0;
if (cdrom_drives[id].bus_type > CDROM_BUS_ATAPI_PIO_AND_DMA)
{
cdrom_drives[id].bus_mode |= 2;
}
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
{
cdrom_drives[id].bus_mode |= 1;
}
cdrom_log("CD-ROM %i: Bus type %i, bus mode %i\n", id, cdrom_drives[id].bus_type, cdrom_drives[id].bus_mode);
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
{
cdrom_set_signature(id);
cdrom_drives[id].max_blocks_at_once = 1;
}
else
{
cdrom_drives[id].max_blocks_at_once = 85;
}
cdrom[id].status = READY_STAT | DSC_STAT;
cdrom[id].pos = 0;
cdrom[id].packet_status = 0xff;
cdrom_sense_key = cdrom_asc = cdrom_ascq = cdrom[id].unit_attention = 0;
cdrom[id].cdb_len_setting = 0;
cdrom[id].cdb_len = 12;
}
int cdrom_supports_pio(int id)
{
return (cdrom_drives[id].bus_mode & 1);
}
int cdrom_supports_dma(int id)
{
return (cdrom_drives[id].bus_mode & 2);
}
/* Returns: 0 for none, 1 for PIO, 2 for DMA. */
int cdrom_current_mode(int id)
{
if (!cdrom_supports_pio(id) && !cdrom_supports_dma(id))
{
return 0;
}
if (cdrom_supports_pio(id) && !cdrom_supports_dma(id))
{
return 1;
}
if (!cdrom_supports_pio(id) && cdrom_supports_dma(id))
{
return 2;
}
if (cdrom_supports_pio(id) && cdrom_supports_dma(id))
{
return (cdrom[id].features & 1) ? 2 : 1;
}
return 0;
}
/* Translates ATAPI status (ERR_STAT flag) to SCSI status. */
int cdrom_CDROM_PHASE_to_scsi(uint8_t id)
{
if (cdrom[id].status & ERR_STAT)
{
return SCSI_STATUS_CHECK_CONDITION;
}
else
{
return SCSI_STATUS_OK;
}
}
/* Translates ATAPI phase (DRQ, I/O, C/D) to SCSI phase (MSG, C/D, I/O). */
int cdrom_atapi_phase_to_scsi(uint8_t id)
{
if (cdrom[id].status & 8)
{
switch (cdrom[id].phase & 3)
{
case 0:
return 0;
case 1:
return 2;
case 2:
return 1;
case 3:
return 7;
}
}
else
{
if ((cdrom[id].phase & 3) == 3)
{
return 3;
}
else
{
/* Translate reserved ATAPI phase to reserved SCSI phase. */
return 4;
}
}
return 0;
}
int cdrom_lba_to_msf_accurate(int lba)
{
int temp_pos;
int m, s, f;
temp_pos = lba + 150;
f = temp_pos % 75;
temp_pos -= f;
temp_pos /= 75;
s = temp_pos % 60;
temp_pos -= s;
temp_pos /= 60;
m = temp_pos;
return ((m << 16) | (s << 8) | f);
}
uint32_t cdrom_mode_sense_get_channel(uint8_t id, int channel)
{
return cdrom_mode_sense_pages_saved[id][GPMODE_CDROM_AUDIO_PAGE][channel ? 10 : 8];
}
uint32_t cdrom_mode_sense_get_volume(uint8_t id, int channel)
{
return cdrom_mode_sense_pages_saved[id][GPMODE_CDROM_AUDIO_PAGE][channel ? 11 : 9];
}
void cdrom_mode_sense_load(uint8_t id)
{
FILE *f;
switch(id)
{
case 0:
f = _wfopen(nvr_concat(L"cdrom_1_mode_sense.bin"), L"rb");
break;
case 1:
f = _wfopen(nvr_concat(L"cdrom_2_mode_sense.bin"), L"rb");
break;
case 2:
f = _wfopen(nvr_concat(L"cdrom_3_mode_sense.bin"), L"rb");
break;
case 3:
f = _wfopen(nvr_concat(L"cdrom_4_mode_sense.bin"), L"rb");
break;
default:
return;
}
if (!f)
{
return;
}
fread(cdrom_mode_sense_pages_saved[id][GPMODE_CDROM_AUDIO_PAGE], 1, 0x10, f);
fclose(f);
}
void cdrom_mode_sense_save(uint8_t id)
{
FILE *f;
switch(id)
{
case 0:
f = _wfopen(nvr_concat(L"cdrom_1_mode_sense.bin"), L"wb");
break;
case 1:
f = _wfopen(nvr_concat(L"cdrom_2_mode_sense.bin"), L"wb");
break;
case 2:
f = _wfopen(nvr_concat(L"cdrom_3_mode_sense.bin"), L"wb");
break;
case 3:
f = _wfopen(nvr_concat(L"cdrom_4_mode_sense.bin"), L"wb");
break;
default:
return;
}
if (!f)
{
return;
}
fwrite(cdrom_mode_sense_pages_saved[id][GPMODE_CDROM_AUDIO_PAGE], 1, 0x10, f);
fclose(f);
}
int cdrom_mode_select_init(uint8_t id, uint8_t command, uint16_t pl_length, uint8_t do_save)
{
switch(command)
{
case GPCMD_MODE_SELECT_6:
cdrom[id].current_page_len = 4;
break;
case GPCMD_MODE_SELECT_10:
cdrom[id].current_page_len = 8;
break;
default:
cdrom_log("CD-ROM %i: Attempting to initialize MODE SELECT with unrecognized command: %02X\n", id, command);
return -1;
}
if (pl_length == 0)
{
cdrom_log("CD-ROM %i: Attempting to initialize MODE SELECT with zero parameter list length: %02X\n", id, command);
return -2;
}
cdrom[id].current_page_pos = 0;
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_HEADER;
cdrom[id].total_length = pl_length;
cdrom[id].written_length = 0;
cdrom[id].do_page_save = do_save;
return 1;
}
int cdrom_mode_select_terminate(uint8_t id, int force)
{
if (((cdrom[id].written_length >= cdrom[id].total_length) || force) && (cdrom[id].mode_select_phase != MODE_SELECT_PHASE_IDLE))
{
cdrom_log("CD-ROM %i: MODE SELECT terminate: %i\n", id, force);
cdrom[id].current_page_pos = cdrom[id].current_page_len = cdrom[id].block_descriptor_len = 0;
cdrom[id].total_length = cdrom[id].written_length = 0;
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_IDLE;
if (force)
{
cdrom_mode_sense_load(id);
}
return 1;
}
else
{
return 0;
}
}
int cdrom_mode_select_header(uint8_t id, uint8_t val)
{
if (cdrom[id].current_page_pos == 0)
{
cdrom[id].block_descriptor_len = 0;
}
else if (cdrom[id].current_page_pos == (cdrom[id].current_page_len - 2))
{
if ((cdrom_drives[id].bus_type == CDROM_BUS_SCSI) && (cdrom[id].current_page_len == 8))
{
cdrom[id].block_descriptor_len |= ((uint16_t) val) << 8;
cdrom_log("CD-ROM %i: Position: %02X, value: %02X, block descriptor length: %02X\n", id, cdrom[id].current_page_pos, val, cdrom[id].block_descriptor_len);
}
}
else if (cdrom[id].current_page_pos == (cdrom[id].current_page_len - 1))
{
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
cdrom[id].block_descriptor_len |= (uint16_t) val;
cdrom_log("CD-ROM %i: Position: %02X, value: %02X, block descriptor length: %02X\n", id, cdrom[id].current_page_pos, val, cdrom[id].block_descriptor_len);
}
}
cdrom[id].current_page_pos++;
if (cdrom[id].current_page_pos >= cdrom[id].current_page_len)
{
cdrom[id].current_page_pos = 0;
if (cdrom[id].block_descriptor_len)
{
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_BLOCK_DESC;
}
else
{
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_PAGE_HEADER;
}
}
return 1;
}
int cdrom_mode_select_block_desc(uint8_t id)
{
cdrom[id].current_page_pos++;
if (cdrom[id].current_page_pos >= 8)
{
cdrom[id].current_page_pos = 0;
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_PAGE_HEADER;
}
return 1;
}
static void cdrom_invalid_field_pl(uint8_t id);
int cdrom_mode_select_page_header(uint8_t id, uint8_t val)
{
if (cdrom[id].current_page_pos == 0)
{
cdrom[id].current_page_code = val & 0x3f;
if (!(cdrom_mode_sense_page_flags[id] & (1LL << cdrom[id].current_page_code)))
{
cdrom_log("CD-ROM %i: Trying to modify an unimplemented page: %02X\n", id, cdrom[id].current_page_code);
cdrom_mode_select_terminate(id, 1);
cdrom_invalid_field_pl(id);
}
cdrom[id].current_page_pos++;
}
else if (cdrom[id].current_page_pos == 1)
{
cdrom[id].current_page_pos = 0;
cdrom[id].current_page_len = val;
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_PAGE;
}
return 1;
}
int cdrom_mode_select_page(uint8_t id, uint8_t val)
{
if (cdrom_mode_sense_pages_changeable[id][cdrom[id].current_page_code][cdrom[id].current_page_pos + 2] != 0xFF)
{
if (val != cdrom_mode_sense_pages_saved[id][cdrom[id].current_page_code][cdrom[id].current_page_pos + 2])
{
/* Trying to change an unchangeable value. */
cdrom_log("CD-ROM %i: Trying to change an unchangeable value: [%02X][%02X] = %02X (new: %02X)\n", id, cdrom[id].current_page_code, cdrom[id].current_page_pos + 2, cdrom_mode_sense_pages_saved[id][cdrom[id].current_page_code][cdrom[id].current_page_pos + 2], val);
cdrom_mode_select_terminate(id, 1);
cdrom_invalid_field_pl(id);
return 0;
}
}
else
{
if (cdrom[id].current_page_code == 0xE)
{
if ((cdrom[id].current_page_pos == 6) || (cdrom[id].current_page_pos == 8))
{
if (val > 3)
{
/* Trying to set an unsupported audio channel. */
cdrom_log("CD-ROM %i: Trying to set an unsupported value: [%02X][%02X] = %02X (new: %02X)\n", id, cdrom[id].current_page_code, cdrom[id].current_page_pos, cdrom_mode_sense_pages_saved[id][cdrom[id].current_page_code][cdrom[id].current_page_pos + 2], val);
return 0;
}
}
}
cdrom_mode_sense_pages_saved[id][cdrom[id].current_page_code][cdrom[id].current_page_pos + 2] = val;
}
cdrom[id].current_page_pos++;
if (cdrom[id].current_page_pos >= cdrom[id].current_page_len)
{
cdrom[id].current_page_pos = 0;
cdrom[id].mode_select_phase = MODE_SELECT_PHASE_PAGE_HEADER;
}
return 1;
}
static void cdrom_command_complete(uint8_t id);
int cdrom_mode_select_write(uint8_t id, uint8_t val)
{
int ret = 0;
int ret2 = 0;
if (id > CDROM_NUM)
{
cdrom_log("MODE SELECT: attempted write to wrong CD-ROM drive\n", val);
return -6;
}
if (cdrom[id].total_length == 0)
{
cdrom_log("CD-ROM %i: MODE SELECT: attempted write when not initialized (%02X)\n", id, val);
return -3;
}
cdrom[id].written_length++;
switch (cdrom[id].mode_select_phase)
{
case MODE_SELECT_PHASE_IDLE:
cdrom_log("CD-ROM %i: MODE SELECT idle (%02X)\n", id, val);
ret = 1;
break;
case MODE_SELECT_PHASE_HEADER:
cdrom_log("CD-ROM %i: MODE SELECT header (%02X)\n", id, val);
ret = cdrom_mode_select_header(id, val);
break;
case MODE_SELECT_PHASE_BLOCK_DESC:
cdrom_log("CD-ROM %i: MODE SELECT block descriptor (%02X)\n", id, val);
ret = cdrom_mode_select_block_desc(id);
break;
case MODE_SELECT_PHASE_PAGE_HEADER:
cdrom_log("CD-ROM %i: MODE SELECT page header (%02X)\n", id, val);
ret = cdrom_mode_select_page_header(id, val);
break;
case MODE_SELECT_PHASE_PAGE:
cdrom_log("CD-ROM %i: MODE SELECT page (%02X)\n", id, val);
ret = cdrom_mode_select_page(id, val);
if (cdrom[id].mode_select_phase == MODE_SELECT_PHASE_PAGE_HEADER)
{
if (cdrom[id].do_page_save && (cdrom_mode_sense_pages_default[id][cdrom[id].current_page_code][0] & 0x80))
{
cdrom_log("CD-ROM %i: Page %i finished, saving it...\n", id, cdrom[id].current_page_code);
cdrom_mode_sense_save(id);
}
}
break;
default:
cdrom_log("CD-ROM %i: MODE SELECT unknown phase (%02X)\n", id, val);
ret = -4;
break;
}
/* On termination, override the return value, but only if it is 1. */
ret2 = cdrom_mode_select_terminate(id, 0);
if (ret2)
{
cdrom_command_complete(id);
}
if (ret2 && (ret == 1))
{
ret = -5;
}
return ret;
}
uint8_t cdrom_read_capacity_cdb[12] = {0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
static int cdrom_pass_through(uint8_t id, uint32_t *len, uint8_t *cdb, uint8_t *buffer);
int cdrom_read_capacity(uint8_t id, uint8_t *cdb, uint8_t *buffer, uint32_t *len)
{
int ret = 0;
int size = 0;
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, len, cdb, buffer);
if (!ret)
{
return 0;
}
if (*len == 65534)
{
*len = 8;
}
}
else
{
size = cdrom_drives[id].handler->size(id) - 1; /* IMPORTANT: What's returned is the last LBA block. */
memset(buffer, 0, 8);
buffer[0] = (size >> 24) & 0xff;
buffer[1] = (size >> 16) & 0xff;
buffer[2] = (size >> 8) & 0xff;
buffer[3] = size & 0xff;
buffer[6] = 8; /* 2048 = 0x0800 */
*len = 8;
}
return 1;
}
/*SCSI Mode Sense 6/10*/
uint8_t cdrom_mode_sense_read(uint8_t id, uint8_t page_control, uint8_t page, uint8_t pos)
{
switch (page_control)
{
case 0:
case 3:
return cdrom_mode_sense_pages_saved[id][page][pos];
break;
case 1:
return cdrom_mode_sense_pages_changeable[id][page][pos];
break;
case 2:
return cdrom_mode_sense_pages_default[id][page][pos];
break;
}
return 0;
}
uint32_t cdrom_mode_sense(uint8_t id, uint8_t *buf, uint32_t pos, uint8_t type, uint8_t block_descriptor_len)
{
uint8_t page_control = (type >> 6) & 3;
int i = 0;
int j = 0;
uint8_t msplen;
type &= 0x3f;
if (block_descriptor_len)
{
buf[pos++] = 1; /* Density code. */
buf[pos++] = 0; /* Number of blocks (0 = all). */
buf[pos++] = 0;
buf[pos++] = 0;
buf[pos++] = 0; /* Reserved. */
buf[pos++] = 0; /* Block length (0x800 = 2048 bytes). */
buf[pos++] = 8;
buf[pos++] = 0;
}
for (i = 0; i < 0x40; i++)
{
if ((type == GPMODE_ALL_PAGES) || (type == i))
{
if (cdrom_mode_sense_page_flags[id] & (1LL << cdrom[id].current_page_code))
{
buf[pos++] = cdrom_mode_sense_read(id, page_control, i, 0);
msplen = cdrom_mode_sense_read(id, page_control, i, 1);
buf[pos++] = msplen;
cdrom_log("CD-ROM %i: MODE SENSE: Page [%02X] length %i\n", id, i, msplen);
for (j = 0; j < msplen; j++)
{
buf[pos++] = cdrom_mode_sense_read(id, page_control, i, 2 + j);
}
}
}
}
return pos;
}
void cdrom_update_request_length(uint8_t id, int len, int block_len)
{
/* For media access commands, make sure the requested DRQ length matches the block length. */
switch (cdrom[id].current_cdb[0])
{
case 0x08:
case 0x28:
case 0xa8:
case 0xb9:
case 0xbe:
if (cdrom[id].request_length < block_len)
{
cdrom[id].request_length = block_len;
}
/* Make sure we respect the limit of how many blocks we can transfer at once. */
if (cdrom[id].requested_blocks > cdrom_drives[id].max_blocks_at_once)
{
cdrom[id].requested_blocks = cdrom_drives[id].max_blocks_at_once;
}
cdrom[id].block_total = (cdrom[id].requested_blocks * block_len);
if (len > cdrom[id].block_total)
{
len = cdrom[id].block_total;
}
break;
default:
cdrom[id].packet_len = len;
break;
}
/* If the DRQ length is odd, and the total remaining length is bigger, make sure it's even. */
if ((cdrom[id].request_length & 1) && (cdrom[id].request_length < len))
{
cdrom[id].request_length &= 0xfffe;
}
/* If the DRQ length is smaller or equal in size to the total remaining length, set it to that. */
if (len <= cdrom[id].request_length)
{
cdrom[id].request_length = len;
}
return;
}
static int cdrom_is_media_access(uint8_t id)
{
switch (cdrom[id].current_cdb[0])
{
case 0x08:
case 0x28:
case 0xa8:
case 0xb9:
case 0xbe:
return 1;
default:
return 0;
}
}
static void cdrom_command_common(uint8_t id)
{
cdrom[id].status = BUSY_STAT;
cdrom[id].phase = 1;
cdrom[id].pos = 0;
if (cdrom[id].packet_status == CDROM_PHASE_COMPLETE)
{
cdrom[id].callback = 20 * CDROM_TIME;
}
else if (cdrom[id].packet_status == CDROM_PHASE_DATA_IN)
{
if (cdrom[id].current_cdb[0] == 0x42)
{
cdrom_log("CD-ROM %i: READ SUBCHANNEL\n");
cdrom[id].callback = 1000 * CDROM_TIME;
}
else
{
cdrom[id].callback = 60 * CDROM_TIME;
}
}
else
{
cdrom[id].callback = 60 * CDROM_TIME;
}
}
static void cdrom_command_complete(uint8_t id)
{
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom_command_common(id);
}
static void cdrom_command_read(uint8_t id)
{
cdrom[id].packet_status = CDROM_PHASE_DATA_IN;
cdrom_command_common(id);
cdrom[id].total_read = 0;
}
static void cdrom_command_read_dma(uint8_t id)
{
cdrom[id].packet_status = CDROM_PHASE_DATA_IN_DMA;
cdrom_command_common(id);
cdrom[id].total_read = 0;
}
static void cdrom_command_write(uint8_t id)
{
cdrom[id].packet_status = CDROM_PHASE_DATA_OUT;
cdrom_command_common(id);
}
static void cdrom_command_write_dma(uint8_t id)
{
cdrom[id].packet_status = CDROM_PHASE_DATA_OUT_DMA;
cdrom_command_common(id);
}
static int cdrom_request_length_is_zero(uint8_t id)
{
if ((cdrom[id].request_length == 0) && (cdrom_drives[id].bus_type < CDROM_BUS_SCSI))
{
return 1;
}
return 0;
}
static void cdrom_data_command_finish(uint8_t id, int len, int block_len, int alloc_len, int direction)
{
cdrom_log("CD-ROM %i: Finishing command (%02X): %i, %i, %i, %i, %i\n", id, cdrom[id].current_cdb[0], len, block_len, alloc_len, direction, cdrom[id].request_length);
cdrom[id].pos=0;
if (alloc_len >= 0)
{
if (alloc_len < len)
{
len = alloc_len;
}
}
if (cdrom_request_length_is_zero(id) || (len == 0) || (cdrom_current_mode(id) == 0))
{
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].InitLength = 0;
}
else
{
cdrom[id].init_length = 0;
}
cdrom_command_complete(id);
}
else
{
if (cdrom_current_mode(id) == 2)
{
if (direction == 0)
{
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].InitLength = alloc_len;
}
else
{
cdrom[id].init_length = alloc_len;
}
cdrom_command_read_dma(id);
}
else
{
cdrom_command_write_dma(id);
}
}
else
{
cdrom_update_request_length(id, len, block_len);
if (direction == 0)
{
cdrom_command_read(id);
}
else
{
cdrom_command_write(id);
}
}
}
cdrom_log("CD-ROM %i: Status: %i, cylinder %i, packet length: %i, position: %i, phase: %i\n", id, cdrom[id].packet_status, cdrom[id].request_length, cdrom[id].packet_len, cdrom[id].pos, cdrom[id].phase);
}
static void cdrom_sense_clear(int id, int command)
{
cdrom[id].previous_command = command;
cdrom_sense_key = cdrom_asc = cdrom_ascq = 0;
}
static void cdrom_cmd_error(uint8_t id)
{
cdrom[id].error = ((cdrom_sense_key & 0xf) << 4) | ABRT_ERR;
if (cdrom[id].unit_attention)
{
cdrom[id].error |= MCR_ERR;
}
cdrom[id].status = READY_STAT | ERR_STAT;
cdrom[id].phase = 3;
cdrom[id].packet_status = 0x80;
cdrom[id].callback = 50 * CDROM_TIME;
2017-01-21 01:19:49 +01:00
cdrom_log("CD-ROM %i: ERROR: %02X/%02X/%02X\n", id, cdrom_sense_key, cdrom_asc, cdrom_ascq);
}
static void cdrom_unit_attention(uint8_t id)
{
cdrom[id].error = (SENSE_UNIT_ATTENTION << 4) | ABRT_ERR;
if (cdrom[id].unit_attention)
{
cdrom[id].error |= MCR_ERR;
}
cdrom[id].status = READY_STAT | ERR_STAT;
cdrom[id].phase = 3;
cdrom[id].packet_status = 0x80;
cdrom[id].callback = 50 * CDROM_TIME;
cdrom_log("CD-ROM %i: UNIT ATTENTION\n", id);
}
static void cdrom_not_ready(uint8_t id)
{
cdrom_sense_key = SENSE_NOT_READY;
cdrom_asc = ASC_MEDIUM_NOT_PRESENT;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_invalid_lun(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_INV_LUN;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_illegal_opcode(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_ILLEGAL_OPCODE;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_lba_out_of_range(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_LBA_OUT_OF_RANGE;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_invalid_field(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_INV_FIELD_IN_CMD_PACKET;
cdrom_ascq = 0;
cdrom_cmd_error(id);
cdrom[id].status = 0x53;
}
static void cdrom_invalid_field_pl(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_INV_FIELD_IN_PARAMETER_LIST;
cdrom_ascq = 0;
cdrom_cmd_error(id);
cdrom[id].status = 0x53;
}
static void cdrom_illegal_mode(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_ILLEGAL_MODE_FOR_THIS_TRACK;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_incompatible_format(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_INCOMPATIBLE_FORMAT;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static void cdrom_data_phase_error(uint8_t id)
{
cdrom_sense_key = SENSE_ILLEGAL_REQUEST;
cdrom_asc = ASC_DATA_PHASE_ERROR;
cdrom_ascq = 0;
cdrom_cmd_error(id);
}
static int cdrom_pass_through(uint8_t id, uint32_t *len, uint8_t *cdb, uint8_t *buffer)
{
int ret = 0;
uint8_t temp_cdb[16];
memset(temp_cdb, 0, 16);
if (cdb[0] == 8)
{
temp_cdb[0] = 0x28;
temp_cdb[8] = cdb[4];
temp_cdb[3] = cdb[1];
temp_cdb[4] = cdb[2];
temp_cdb[5] = cdb[3];
}
else
{
memcpy(temp_cdb, cdb, 16);
}
ret = cdrom_drives[id].handler->pass_through(id, temp_cdb, buffer, len);
cdrom_log("CD-ROM %i: Data from pass through: %02X %02X %02X %02X %02X %02X %02X %02X\n", id, buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7]);
cdrom_log("CD-ROM %i: Returned value: %i\n", id, ret);
if (!ret)
{
/* Command failed with OS error code, return illegal opcode. */
cdrom_log("CD-ROM %i: Command failed with OS error code, return illegal opcode.\n", id);
cdrom_illegal_opcode(id);
return 0;
}
else
{
if ((cdrom_sense_key != 0) || (cdrom_asc != 0) || (cdrom_ascq != 0))
{
/* Command failed with sense, error with that sense. */
cdrom_log("CD-ROM %i: Command failed with sense, error with that sense (%02X/%02X/%02X).\n", id, cdrom_sense_key, cdrom_asc, cdrom_ascq);
cdrom_cmd_error(id);
return 0;
}
else
{
/* Command was performed successfully. */
cdrom_log("CD-ROM %i: Command was performed successfully.\n", id);
return 1;
}
}
}
void cdrom_update_cdb(uint8_t *cdb, int lba_pos, int number_of_blocks)
{
int temp = 0;
switch(cdb[0])
{
case GPCMD_READ_6:
cdb[1] = (lba_pos >> 16) & 0xff;
cdb[2] = (lba_pos >> 8) & 0xff;
cdb[3] = lba_pos & 0xff;
break;
case GPCMD_READ_10:
cdb[2] = (lba_pos >> 24) & 0xff;
cdb[3] = (lba_pos >> 16) & 0xff;
cdb[4] = (lba_pos >> 8) & 0xff;
cdb[5] = lba_pos & 0xff;
cdb[7] = (number_of_blocks >> 8) & 0xff;
cdb[8] = number_of_blocks & 0xff;
break;
case GPCMD_READ_12:
cdb[2] = (lba_pos >> 24) & 0xff;
cdb[3] = (lba_pos >> 16) & 0xff;
cdb[4] = (lba_pos >> 8) & 0xff;
cdb[5] = lba_pos & 0xff;
cdb[6] = (number_of_blocks >> 24) & 0xff;
cdb[7] = (number_of_blocks >> 16) & 0xff;
cdb[8] = (number_of_blocks >> 8) & 0xff;
cdb[9] = number_of_blocks & 0xff;
break;
case GPCMD_READ_CD_MSF:
temp = cdrom_lba_to_msf_accurate(lba_pos);
cdb[3] = (temp >> 16) & 0xff;
cdb[4] = (temp >> 8) & 0xff;
cdb[5] = temp & 0xff;
temp = cdrom_lba_to_msf_accurate(lba_pos + number_of_blocks - 1);
cdb[6] = (temp >> 16) & 0xff;
cdb[7] = (temp >> 8) & 0xff;
cdb[8] = temp & 0xff;
break;
case GPCMD_READ_CD:
cdb[2] = (lba_pos >> 24) & 0xff;
cdb[3] = (lba_pos >> 16) & 0xff;
cdb[4] = (lba_pos >> 8) & 0xff;
cdb[5] = lba_pos & 0xff;
cdb[6] = (number_of_blocks >> 16) & 0xff;
cdb[7] = (number_of_blocks >> 8) & 0xff;
cdb[8] = number_of_blocks & 0xff;
break;
}
}
int cdrom_read_data(uint8_t id, int msf, int type, int flags, uint32_t *len)
{
uint8_t *cdbufferb = (uint8_t *) cdrom[id].buffer;
int ret = 0;
int cdsize = 0;
int i = 0;
int temp_len = 0;
int last_valid_data_pos = 0;
if (cdrom_drives[id].handler->pass_through)
{
cdsize = cdrom_drives[id].handler->size(id);
ret = cdrom_pass_through(id, len, cdrom[id].current_cdb, cdbufferb + cdrom[id].data_pos);
cdrom[id].data_pos += *len;
if (!ret)
{
return 0;
}
if (cdrom[id].sector_pos > (cdsize - 1))
{
/* cdrom_log("CD-ROM %i: Trying to read beyond the end of disc\n", id); */
cdrom_lba_out_of_range(id);
return 0;
}
cdrom[id].old_len = *len;
}
else
{
if (cdrom[id].sector_pos > (cdrom_drives[id].handler->size(id) - 1))
{
/* cdrom_log("CD-ROM %i: Trying to read beyond the end of disc\n", id); */
cdrom_lba_out_of_range(id);
return 0;
}
cdrom[id].old_len = 0;
*len = 0;
for (i = 0; i < cdrom[id].requested_blocks; i++)
{
ret = cdrom_drives[id].handler->readsector_raw(id, cdbufferb + cdrom[id].data_pos, cdrom[id].sector_pos + i, msf, type, flags, &temp_len);
last_valid_data_pos = cdrom[id].data_pos;
cdrom[id].data_pos += temp_len;
cdrom[id].old_len += temp_len;
*len += temp_len;
if (!ret)
{
cdrom_illegal_mode(id);
return 0;
}
}
cdrom_log("CD-ROM %i: Data from raw sector read: %02X %02X %02X %02X %02X %02X %02X %02X\n", id, cdbufferb[last_valid_data_pos + 0], cdbufferb[last_valid_data_pos + 1], cdbufferb[last_valid_data_pos + 2], cdbufferb[last_valid_data_pos + 3], cdbufferb[last_valid_data_pos + 4], cdbufferb[last_valid_data_pos + 5], cdbufferb[last_valid_data_pos + 6], cdbufferb[last_valid_data_pos + 7]);
}
return 1;
}
int cdrom_read_blocks(uint8_t id, uint32_t *len, int first_batch)
{
int ret = 0;
int msf = 0;
int type = 0;
int flags = 0;
if (cdrom[id].current_cdb[0] == 0xb9)
{
msf = 1;
}
if ((cdrom[id].current_cdb[0] == 0xb9) || (cdrom[id].current_cdb[0] == 0xbe))
{
type = (cdrom[id].current_cdb[1] >> 2) & 7;
flags = cdrom[id].current_cdb[9] || (((uint32_t) cdrom[id].current_cdb[10]) << 8);
}
else
{
type = 8;
flags = 0x10;
}
cdrom[id].data_pos = 0;
if (!cdrom[id].sector_len)
{
cdrom_command_complete(id);
return -1;
}
cdrom_log("Reading %i blocks starting from %i...\n", cdrom[id].requested_blocks, cdrom[id].sector_pos);
cdrom_update_cdb(cdrom[id].current_cdb, cdrom[id].sector_pos, cdrom[id].requested_blocks);
ret = cdrom_read_data(id, msf, type, flags, len);
cdrom_log("Read %i bytes of blocks...\n", *len);
if (!ret || ((cdrom[id].old_len != *len) && !first_batch))
{
if ((cdrom[id].old_len != *len) && !first_batch)
{
cdrom_illegal_mode(id);
}
return 0;
}
cdrom[id].sector_pos += cdrom[id].requested_blocks;
cdrom[id].sector_len -= cdrom[id].requested_blocks;
return 1;
}
/*SCSI Get Configuration*/
uint8_t cdrom_set_profile(uint8_t *buf, uint8_t *index, uint16_t profile)
{
uint8_t *buf_profile = buf + 12; /* start of profiles */
buf_profile += ((*index) * 4); /* start of indexed profile */
buf_profile[0] = (profile >> 8) & 0xff;
buf_profile[1] = profile & 0xff;
buf_profile[2] = ((buf_profile[0] == buf[6]) && (buf_profile[1] == buf[7]));
/* each profile adds 4 bytes to the response */
(*index)++;
buf[11] += 4; /* Additional Length */
return 4;
}
/*SCSI Read DVD Structure*/
static int cdrom_read_dvd_structure(uint8_t id, int format, const uint8_t *packet, uint8_t *buf)
{
int layer = packet[6];
uint64_t total_sectors;
switch (format)
{
case 0x00: /* Physical format information */
total_sectors = (uint64_t) cdrom_drives[id].handler->size(id);
if (layer != 0)
{
cdrom_invalid_field(id);
return 0;
}
total_sectors >>= 2;
if (total_sectors == 0)
{
/* return -ASC_MEDIUM_NOT_PRESENT; */
cdrom_not_ready(id);
return 0;
}
buf[4] = 1; /* DVD-ROM, part version 1 */
buf[5] = 0xf; /* 120mm disc, minimum rate unspecified */
buf[6] = 1; /* one layer, read-only (per MMC-2 spec) */
buf[7] = 0; /* default densities */
/* FIXME: 0x30000 per spec? */
buf[8] = buf[9] = buf[10] = buf[11] = 0; /* start sector */
buf[12] = (total_sectors >> 24) & 0xff; /* end sector */
buf[13] = (total_sectors >> 16) & 0xff;
buf[14] = (total_sectors >> 8) & 0xff;
buf[15] = total_sectors & 0xff;
buf[16] = (total_sectors >> 24) & 0xff; /* l0 end sector */
buf[17] = (total_sectors >> 16) & 0xff;
buf[18] = (total_sectors >> 8) & 0xff;
buf[19] = total_sectors & 0xff;
/* Size of buffer, not including 2 byte size field */
buf[0] = ((2048 +2 ) >> 8) & 0xff;
buf[1] = (2048 + 2) & 0xff;
/* 2k data + 4 byte header */
return (2048 + 4);
case 0x01: /* DVD copyright information */
buf[4] = 0; /* no copyright data */
buf[5] = 0; /* no region restrictions */
/* Size of buffer, not including 2 byte size field */
buf[0] = ((4 + 2) >> 8) & 0xff;
buf[1] = (4 + 2) & 0xff;
/* 4 byte header + 4 byte data */
return (4 + 4);
case 0x03: /* BCA information - invalid field for no BCA info */
cdrom_invalid_field(id);
return 0;
case 0x04: /* DVD disc manufacturing information */
/* Size of buffer, not including 2 byte size field */
buf[0] = ((2048 + 2) >> 8) & 0xff;
buf[1] = (2048 + 2) & 0xff;
/* 2k data + 4 byte header */
return (2048 + 4);
case 0xff:
/*
* This lists all the command capabilities above. Add new ones
* in order and update the length and buffer return values.
*/
buf[4] = 0x00; /* Physical format */
buf[5] = 0x40; /* Not writable, is readable */
buf[6] = ((2048 + 4) >> 8) & 0xff;
buf[7] = (2048 + 4) & 0xff;
buf[8] = 0x01; /* Copyright info */
buf[9] = 0x40; /* Not writable, is readable */
buf[10] = ((4 + 4) >> 8) & 0xff;
buf[11] = (4 + 4) & 0xff;
buf[12] = 0x03; /* BCA info */
buf[13] = 0x40; /* Not writable, is readable */
buf[14] = ((188 + 4) >> 8) & 0xff;
buf[15] = (188 + 4) & 0xff;
buf[16] = 0x04; /* Manufacturing info */
buf[17] = 0x40; /* Not writable, is readable */
buf[18] = ((2048 + 4) >> 8) & 0xff;
buf[19] = (2048 + 4) & 0xff;
/* Size of buffer, not including 2 byte size field */
buf[6] = ((16 + 2) >> 8) & 0xff;
buf[7] = (16 + 2) & 0xff;
/* data written + 4 byte header */
return (16 + 4);
default: /* TODO: formats beyond DVD-ROM requires */
cdrom_invalid_field(id);
return 0;
}
}
/*SCSI Get Event Status Notification*/
static uint32_t cdrom_get_event_status(uint8_t id, uint8_t *buffer)
{
uint8_t event_code, media_status = 0;
if (buffer[5])
{
media_status = MS_TRAY_OPEN;
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
}
else
{
media_status = MS_MEDIA_PRESENT;
}
event_code = MEC_NO_CHANGE;
if (media_status != MS_TRAY_OPEN)
{
if (!buffer[4])
{
event_code = MEC_NEW_MEDIA;
cdrom_drives[id].handler->load(id);
}
else if (buffer[4]==2)
{
event_code = MEC_EJECT_REQUESTED;
cdrom_drives[id].handler->eject(id);
}
}
buffer[4] = event_code;
buffer[5] = media_status;
buffer[6] = 0;
buffer[7] = 0;
return 8;
}
void cdrom_insert(uint8_t id)
{
cdrom[id].unit_attention = 1;
}
/*SCSI Sense Initialization*/
void cdrom_sense_code_ok(uint8_t id)
{
cdrom_sense_key = SENSE_NONE;
cdrom_asc = 0;
cdrom_ascq = 0;
}
int cdrom_pre_execution_check(uint8_t id, uint8_t *cdb)
{
int ready = 0;
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
if (((cdrom[id].request_length >> 5) & 7) != cdrom_drives[id].scsi_device_lun)
{
cdrom_log("CD-ROM %i: Attempting to execute a unknown command targeted at SCSI LUN %i\n", id, ((cdrom[id].request_length >> 5) & 7));
cdrom_invalid_lun(id);
return 0;
}
}
if (!(cdrom_command_flags[cdb[0]] & IMPLEMENTED))
{
cdrom_log("CD-ROM %i: Attempting to execute unknown command %02X over %s\n", id, cdb[0], (cdrom_drives[id].bus_type == CDROM_BUS_SCSI) ? "SCSI" : ((cdrom_drives[id].bus_type == CDROM_BUS_ATAPI_PIO_AND_DMA) ? "ATAPI PIO/DMA" : "ATAPI PIO"));
cdrom_illegal_opcode(id);
return 0;
}
if ((cdrom_drives[id].bus_type < CDROM_BUS_SCSI) && (cdrom_command_flags[cdb[0]] & SCSI_ONLY))
{
cdrom_log("CD-ROM %i: Attempting to execute SCSI-only command %02X over ATAPI\n", id, cdb[0]);
cdrom_illegal_opcode(id);
return 0;
}
if ((cdrom_drives[id].bus_type == CDROM_BUS_SCSI) && (cdrom_command_flags[cdb[0]] & ATAPI_ONLY))
{
cdrom_log("CD-ROM %i: Attempting to execute ATAPI-only command %02X over SCSI\n", id, cdb[0]);
cdrom_illegal_opcode(id);
return 0;
}
if ((cdrom_drives[id].handler->status(id) == CD_STATUS_PLAYING) || (cdrom_drives[id].handler->status(id) == CD_STATUS_PAUSED))
{
ready = 1;
goto skip_ready_check;
}
if (cdrom_drives[id].handler->medium_changed(id))
{
/* cdrom_log("CD-ROM %i: Medium has changed...\n", id); */
cdrom_insert(id);
}
ready = cdrom_drives[id].handler->ready(id);
skip_ready_check:
if (!ready && cdrom[id].unit_attention)
{
/* If the drive is not ready, there is no reason to keep the
UNIT ATTENTION condition present, as we only use it to mark
disc changes. */
cdrom[id].unit_attention = 0;
}
/* If the UNIT ATTENTION condition is set and the command does not allow
execution under it, error out and report the condition. */
if (cdrom[id].unit_attention == 1)
{
/* Only increment the unit attention phase if the command can not pass through it. */
if (!(cdrom_command_flags[cdb[0]] & ALLOW_UA))
{
/* cdrom_log("CD-ROM %i: Unit attention now 2\n", id); */
cdrom[id].unit_attention = 2;
cdrom_log("CD-ROM %i: UNIT ATTENTION: Command %02X not allowed to pass through\n", id, cdb[0]);
cdrom_unit_attention(id);
return 0;
}
}
else if (cdrom[id].unit_attention == 2)
{
if (cdb[0] != GPCMD_REQUEST_SENSE)
{
/* cdrom_log("CD-ROM %i: Unit attention now 0\n", id); */
cdrom[id].unit_attention = 0;
}
}
/* Unless the command is REQUEST SENSE, clear the sense. This will *NOT*
the UNIT ATTENTION condition if it's set. */
if (cdb[0] != GPCMD_REQUEST_SENSE)
{
cdrom_sense_clear(id, cdb[0]);
}
/* Next it's time for NOT READY. */
if ((cdrom_command_flags[cdb[0]] & CHECK_READY) && !ready)
{
cdrom_log("CD-ROM %i: Not ready (%02X)\n", id, cdb[0]);
cdrom_not_ready(id);
return 0;
}
cdrom_log("CD-ROM %i: Continuing with command %02X\n", id, cdb[0]);
return 1;
}
void cdrom_clear_callback(uint8_t channel)
{
uint8_t id = atapi_cdrom_drives[channel];
if (id <= CDROM_NUM)
{
cdrom[id].callback = 0;
}
}
static void cdrom_seek(uint8_t id, uint32_t pos)
{
/* cdrom_log("CD-ROM %i: Seek %08X\n", id, pos); */
cdrom[id].seek_pos = pos;
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
}
static void cdrom_rezero(uint8_t id)
{
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
cdrom[id].sector_pos = cdrom[id].sector_len = 0;
cdrom_seek(id, 0);
}
void cdrom_reset(uint8_t id)
{
cdrom_rezero(id);
cdrom[id].status = 0;
cdrom[id].callback = 0;
cdrom[id].packet_status = 0xff;
cdrom[id].unit_attention = 0;
}
int cdrom_playing_completed(uint8_t id)
2017-01-21 01:58:48 +01:00
{
cdrom[id].prev_status = cdrom[id].cd_status;
cdrom[id].cd_status = cdrom_drives[id].handler->status(id);
if (((cdrom[id].prev_status == CD_STATUS_PLAYING) || (cdrom[id].prev_status == CD_STATUS_PAUSED)) && ((cdrom[id].cd_status != CD_STATUS_PLAYING) && (cdrom[id].cd_status != CD_STATUS_PAUSED)))
{
return 1;
}
else
{
return 0;
}
}
void cdrom_request_sense(uint8_t id, uint8_t *buffer, uint8_t alloc_length)
{
/*Will return 18 bytes of 0*/
if (alloc_length != 0)
{
memset(buffer, 0, alloc_length);
memcpy(buffer, cdrom[id].sense, alloc_length);
}
buffer[0] = 0x70;
2017-01-21 01:58:48 +01:00
if ((cdrom_sense_key > 0) && ((cdrom[id].cd_status < CD_STATUS_PLAYING) || (cdrom[id].cd_status == CD_STATUS_STOPPED)) && cdrom_playing_completed(id))
{
buffer[2]=SENSE_ILLEGAL_REQUEST;
buffer[12]=ASC_AUDIO_PLAY_OPERATION;
buffer[13]=ASCQ_AUDIO_PLAY_OPERATION_COMPLETED;
}
else if ((cdrom_sense_key == 0) && (cdrom[id].cd_status >= CD_STATUS_PLAYING) && (cdrom[id].cd_status != CD_STATUS_STOPPED))
{
buffer[2]=SENSE_ILLEGAL_REQUEST;
buffer[12]=ASC_AUDIO_PLAY_OPERATION;
buffer[13]=(cdrom[id].cd_status == CD_STATUS_PLAYING) ? ASCQ_AUDIO_PLAY_OPERATION_IN_PROGRESS : ASCQ_AUDIO_PLAY_OPERATION_PAUSED;
}
else
{
if (cdrom[id].unit_attention && (cdrom_sense_key == 0))
{
buffer[2]=SENSE_UNIT_ATTENTION;
buffer[12]=ASC_MEDIUM_MAY_HAVE_CHANGED;
buffer[13]=0;
}
}
/* cdrom_log("CD-ROM %i: Reporting sense: %02X %02X %02X\n", id, cdbufferb[2], cdbufferb[12], cdbufferb[13]); */
if (buffer[2] == SENSE_UNIT_ATTENTION)
{
/* If the last remaining sense is unit attention, clear
that condition. */
cdrom[id].unit_attention = 0;
}
/* Clear the sense stuff as per the spec. */
cdrom_sense_clear(id, GPCMD_REQUEST_SENSE);
}
void cdrom_request_sense_for_scsi(uint8_t id, uint8_t *buffer, uint8_t alloc_length)
{
int ready = 0;
if (cdrom_drives[id].handler->medium_changed(id))
{
/* cdrom_log("CD-ROM %i: Medium has changed...\n", id); */
cdrom_insert(id);
}
ready = cdrom_drives[id].handler->ready(id);
if (!ready && cdrom[id].unit_attention)
{
/* If the drive is not ready, there is no reason to keep the
UNIT ATTENTION condition present, as we only use it to mark
disc changes. */
cdrom[id].unit_attention = 0;
}
/* Do *NOT* advance the unit attention phase. */
cdrom_request_sense(id, buffer, alloc_length);
}
void cdrom_command(uint8_t id, uint8_t *cdb)
{
uint8_t *cdbufferb = (uint8_t *) cdrom[id].buffer;
uint32_t len;
int msf;
int pos=0;
int max_len;
int used_len;
unsigned idx = 0;
unsigned size_idx;
unsigned preamble_len;
int toc_format;
uint32_t alloc_length;
uint8_t index = 0;
int block_desc = 0;
int format = 0;
int ret;
int real_pos;
int track = 0;
char device_identify[9] = { '8', '6', 'B', '_', 'C', 'D', '0', '0', 0 };
char device_identify_ex[15] = { '8', '6', 'B', '_', 'C', 'D', '0', '0', ' ', 'v', '1', '.', '0', '0', 0 };
#if 0
int CdbLength;
#endif
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
cdrom[id].status &= ~ERR_STAT;
}
else
{
cdrom[id].error = 0;
}
cdrom[id].packet_len = 0;
cdrom[id].request_pos = 0;
device_identify[7] = id + 0x30;
device_identify_ex[7] = id + 0x30;
device_identify_ex[10] = EMU_VERSION[0];
device_identify_ex[12] = EMU_VERSION[2];
device_identify_ex[13] = EMU_VERSION[3];
cdrom[id].data_pos = 0;
memcpy(cdrom[id].current_cdb, cdb, cdrom[id].cdb_len);
cdrom[id].cd_status = cdrom_drives[id].handler->status(id);
if (cdb[0] != 0)
{
cdrom_log("CD-ROM %i: Command 0x%02X, Sense Key %02X, Asc %02X, Ascq %02X, %i, Unit attention: %i\n", id, cdb[0], cdrom_sense_key, cdrom_asc, cdrom_ascq, ins, cdrom[id].unit_attention);
cdrom_log("CD-ROM %i: Request length: %04X\n", id, cdrom[id].request_length);
#if 0
for (CdbLength = 1; CdbLength < cdrom[id].cdb_len; CdbLength++)
{
cdrom_log("CD-ROM %i: CDB[%d] = 0x%02X\n", id, CdbLength, cdb[CdbLength]);
}
#endif
}
msf = cdb[1] & 2;
cdrom[id].sector_len = 0;
/* This handles the Not Ready/Unit Attention check if it has to be handled at this point. */
if (cdrom_pre_execution_check(id, cdb) == 0)
{
return;
}
switch (cdb[0])
{
case GPCMD_TEST_UNIT_READY:
cdrom_command_complete(id);
break;
case GPCMD_REZERO_UNIT:
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
cdrom[id].sector_pos = cdrom[id].sector_len = 0;
cdrom_seek(id, 0);
break;
case GPCMD_REQUEST_SENSE:
/* If there's a unit attention condition and there's a buffered not ready, a standalone REQUEST SENSE
should forget about the not ready, and report unit attention straight away. */
2017-09-02 05:41:19 +02:00
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
if (SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].InitLength < cdb[4])
{
cdb[4] = SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].InitLength;
}
}
cdrom_request_sense(id, cdbufferb, cdb[4]);
cdrom_data_command_finish(id, 18, 18, cdb[4], 0);
break;
case GPCMD_SET_SPEED:
case GPCMD_SET_SPEED_ALT:
cdrom_command_complete(id);
break;
case GPCMD_MECHANISM_STATUS:
len = (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
memset(cdbufferb, 0, 8);
cdbufferb[5] = 1;
cdrom_data_command_finish(id, 8, 8, len, 0);
break;
case GPCMD_READ_TOC_PMA_ATIP:
cdrom[id].toctimes++;
max_len = cdb[7];
max_len <<= 8;
max_len |= cdb[8];
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
/* return; */
cdrom_sense_key = cdrom_asc = cdrom_ascq = 0;
goto cdrom_readtoc_fallback;
}
alloc_length = cdbufferb[0];
alloc_length <<= 8;
alloc_length |= cdbufferb[1];
alloc_length += 2;
if (alloc_length < len)
{
len = alloc_length;
}
}
else
{
cdrom_readtoc_fallback:
toc_format = cdb[2] & 0xf;
if (toc_format == 0)
{
toc_format = (cdb[9] >> 6) & 3;
}
switch (toc_format)
{
case 0: /*Normal*/
len = cdrom_drives[id].handler->readtoc(id, cdbufferb, cdb[6], msf, max_len, 0);
break;
case 1: /*Multi session*/
len = cdrom_drives[id].handler->readtoc_session(id, cdbufferb, msf, max_len);
cdbufferb[0] = 0; cdbufferb[1] = 0xA;
break;
case 2: /*Raw*/
len = cdrom_drives[id].handler->readtoc_raw(id, cdbufferb, max_len);
break;
default:
cdrom_invalid_field(id);
return;
}
}
if (len > max_len)
{
len = max_len;
cdbufferb[0] = ((len - 2) >> 8) & 0xff;
cdbufferb[1] = (len - 2) & 0xff;
}
cdrom_data_command_finish(id, len, len, len, 0);
/* cdrom_log("CD-ROM %i: READ_TOC_PMA_ATIP format %02X, length %i (%i)\n", id, toc_format, ide->cylinder, cdbufferb[1]); */
return;
case GPCMD_READ_CD_OLD:
cdrom[id].current_cdb[0] = 0xbe; /* IMPORTANT: Convert the command to new read CD for pass through purposes. */
case GPCMD_READ_6:
case GPCMD_READ_10:
case GPCMD_READ_12:
case GPCMD_READ_CD:
case GPCMD_READ_CD_MSF:
switch(cdb[0])
{
case GPCMD_READ_6:
cdrom[id].sector_len = cdb[4];
cdrom[id].sector_pos = ((((uint32_t) cdb[1]) & 0x1f) << 16) | (((uint32_t) cdb[2]) << 8) | ((uint32_t) cdb[3]);
msf = 0;
break;
case GPCMD_READ_10:
cdrom[id].sector_len = (cdb[7] << 8) | cdb[8];
cdrom[id].sector_pos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
cdrom_log("CD-ROM %i: Length: %i, LBA: %i\n", id, cdrom[id].sector_len, cdrom[id].sector_pos);
msf = 0;
break;
case GPCMD_READ_12:
cdrom[id].sector_len = (((uint32_t) cdb[6]) << 24) | (((uint32_t) cdb[7]) << 16) | (((uint32_t) cdb[8]) << 8) | ((uint32_t) cdb[9]);
cdrom[id].sector_pos = (((uint32_t) cdb[2]) << 24) | (((uint32_t) cdb[3]) << 16) | (((uint32_t) cdb[4]) << 8) | ((uint32_t) cdb[5]);
msf = 0;
break;
case GPCMD_READ_CD_MSF:
/* cdrom_log("CD-ROM %i: Read CD MSF: Start MSF %02X%02X%02X End MSF %02X%02X%02X Flags %02X\n", id, cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]); */
cdrom[id].sector_len = MSFtoLBA(cdb[6], cdb[7], cdb[8]);
cdrom[id].sector_pos = MSFtoLBA(cdb[3], cdb[4], cdb[5]);
cdrom[id].sector_len -= cdrom[id].sector_pos;
cdrom[id].sector_len++;
msf = 1;
break;
case GPCMD_READ_CD_OLD:
case GPCMD_READ_CD:
/* cdrom_log("CD-ROM %i: Read CD: Start LBA %02X%02X%02X%02X Length %02X%02X%02X Flags %02X\n", id, cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9]); */
cdrom[id].sector_len = (cdb[6] << 16) | (cdb[7] << 8) | cdb[8];
cdrom[id].sector_pos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
msf = 0;
break;
}
if (!cdrom[id].sector_len)
{
/* cdrom_log("CD-ROM %i: All done - callback set\n", id); */
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].callback = 20 * CDROM_TIME;
break;
}
max_len = cdrom[id].sector_len;
/* if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI) */
if (cdrom_current_mode(id) == 2)
{
cdrom[id].requested_blocks = max_len;
}
else
{
cdrom[id].requested_blocks = 1;
}
ret = cdrom_read_blocks(id, &alloc_length, 1);
if (ret <= 0)
{
return;
}
cdrom[id].packet_len = max_len * alloc_length;
if (cdrom[id].requested_blocks > 1)
{
cdrom_data_command_finish(id, alloc_length, alloc_length / cdrom[id].requested_blocks, alloc_length, 0);
}
else
{
cdrom_data_command_finish(id, alloc_length, alloc_length, alloc_length, 0);
}
cdrom[id].all_blocks_total = cdrom[id].block_total;
if (cdrom[id].packet_status != CDROM_PHASE_COMPLETE)
{
update_status_bar_icon(SB_CDROM | id, 1);
}
else
{
update_status_bar_icon(SB_CDROM | id, 0);
}
return;
case GPCMD_READ_HEADER:
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
return;
}
}
else
{
cdrom[id].sector_len = (cdb[7] << 8) | cdb[8];
cdrom[id].sector_pos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4]<<8) | cdb[5];
if (msf)
{
real_pos = cdrom_lba_to_msf_accurate(cdrom[id].sector_pos);
}
else
{
real_pos = cdrom[id].sector_pos;
}
cdbufferb[0] = 1; /*2048 bytes user data*/
cdbufferb[1] = cdbufferb[2] = cdbufferb[3] = 0;
cdbufferb[4] = (real_pos >> 24);
cdbufferb[5] = ((real_pos >> 16) & 0xff);
cdbufferb[6] = ((real_pos >> 8) & 0xff);
cdbufferb[7] = real_pos & 0xff;
len = 8;
}
cdrom_data_command_finish(id, len, len, len, 0);
return;
case GPCMD_MODE_SENSE_6:
case GPCMD_MODE_SENSE_10:
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
block_desc = ((cdb[1] >> 3) & 1) ? 0 : 1;
}
else
{
block_desc = 0;
}
if (cdb[0] == GPCMD_MODE_SENSE_6)
{
len = cdb[4];
}
else
{
len = (cdb[8] | (cdb[7] << 8));
}
cdrom[id].current_page_code = cdb[2] & 0x3F;
if (!(cdrom_mode_sense_page_flags[id] & (1LL << cdrom[id].current_page_code)))
{
cdrom_invalid_field(id);
return;
}
memset(cdbufferb, 0, len);
alloc_length = len;
if (cdb[0] == GPCMD_MODE_SENSE_6)
{
len = cdrom_mode_sense(id, cdbufferb, 4, cdb[2], block_desc);
if (len > alloc_length)
{
len = alloc_length;
}
cdbufferb[0] = len - 1;
cdbufferb[1] = cdrom_drives[id].handler->media_type_id(id);
if (block_desc)
{
cdbufferb[3] = 8;
}
}
else
{
len = cdrom_mode_sense(id, cdbufferb, 8, cdb[2], block_desc);
if (len > alloc_length)
{
len = alloc_length;
}
cdbufferb[0]=(len - 2) >> 8;
cdbufferb[1]=(len - 2) & 255;
cdbufferb[2] = cdrom_drives[id].handler->media_type_id(id);
if (block_desc)
{
cdbufferb[6] = 0;
cdbufferb[7] = 8;
}
}
if (len > alloc_length)
{
len = alloc_length;
}
else if (len < alloc_length)
{
alloc_length = len;
}
cdrom_log("CD-ROM %i: Reading mode page: %02X...\n", id, cdb[2]);
cdrom_data_command_finish(id, len, len, alloc_length, 0);
return;
case GPCMD_MODE_SELECT_6:
case GPCMD_MODE_SELECT_10:
if (cdb[0] == GPCMD_MODE_SELECT_6)
{
len = cdb[4];
}
else
{
len = (cdb[7] << 8) | cdb[8];
}
ret = cdrom_mode_select_init(id, cdb[0], len, cdb[1] & 1);
cdrom_data_command_finish(id, len, len, len, 1);
return;
case GPCMD_GET_CONFIGURATION:
/* XXX: could result in alignment problems in some architectures */
len = (cdb[7] << 8) | cdb[8];
alloc_length = len;
index = 0;
/* only feature 0 is supported */
if (cdb[2] != 0 || cdb[3] != 0)
{
cdrom_invalid_field(id);
return;
}
/*
* XXX: avoid overflow for io_buffer if len is bigger than
* the size of that buffer (dimensioned to max number of
* sectors to transfer at once)
*
* Only a problem if the feature/profiles grow.
*/
if (alloc_length > 512) /* XXX: assume 1 sector */
{
alloc_length = 512;
}
memset(cdbufferb, 0, alloc_length);
/*
* the number of sectors from the media tells us which profile
* to use as current. 0 means there is no media
*/
if (len > CD_MAX_SECTORS)
{
cdbufferb[6] = (MMC_PROFILE_DVD_ROM >> 8) & 0xff;
cdbufferb[7] = MMC_PROFILE_DVD_ROM & 0xff;
}
else if (len <= CD_MAX_SECTORS)
{
cdbufferb[6] = (MMC_PROFILE_CD_ROM >> 8) & 0xff;
cdbufferb[7] = MMC_PROFILE_CD_ROM & 0xff;
}
cdbufferb[10] = 0x02 | 0x01; /* persistent and current */
alloc_length = 12; /* headers: 8 + 4 */
alloc_length += cdrom_set_profile(cdbufferb, &index, MMC_PROFILE_DVD_ROM);
alloc_length += cdrom_set_profile(cdbufferb, &index, MMC_PROFILE_CD_ROM);
cdbufferb[0] = ((alloc_length - 4) >> 24) & 0xff;
cdbufferb[1] = ((alloc_length - 4) >> 16) & 0xff;
cdbufferb[2] = ((alloc_length - 4) >> 8) & 0xff;
cdbufferb[3] = (alloc_length - 4) & 0xff;
cdrom_data_command_finish(id, len, len, alloc_length, 0);
break;
case GPCMD_GET_EVENT_STATUS_NOTIFICATION:
gesn_cdb = (void *) cdb;
gesn_event_header = (void *) cdbufferb;
/* It is fine by the MMC spec to not support async mode operations. */
if (!(gesn_cdb->polled & 0x01))
{
/* asynchronous mode */
/* Only polling is supported, asynchronous mode is not. */
cdrom_invalid_field(id);
return;
}
/* polling mode operation */
/*
* These are the supported events.
*
* We currently only support requests of the 'media' type.
* Notification class requests and supported event classes are bitmasks,
* but they are built from the same values as the "notification class"
* field.
*/
gesn_event_header->supported_events = 1 << GESN_MEDIA;
/*
* We use |= below to set the class field; other bits in this byte
* are reserved now but this is useful to do if we have to use the
* reserved fields later.
*/
gesn_event_header->notification_class = 0;
/*
* Responses to requests are to be based on request priority. The
* notification_class_request_type enum above specifies the
* priority: upper elements are higher prio than lower ones.
*/
if (gesn_cdb->class & (1 << GESN_MEDIA))
{
gesn_event_header->notification_class |= GESN_MEDIA;
used_len = cdrom_get_event_status(id, cdbufferb);
}
else
{
gesn_event_header->notification_class = 0x80; /* No event available */
used_len = sizeof(*gesn_event_header);
}
gesn_event_header->len = used_len - sizeof(*gesn_event_header);
cdrom_data_command_finish(id, used_len, used_len, used_len, 0);
break;
case GPCMD_READ_DISC_INFORMATION:
max_len = cdb[7];
max_len <<= 8;
max_len |= cdb[8];
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
return;
}
alloc_length = cdbufferb[0];
alloc_length <<= 8;
alloc_length |= cdbufferb[1];
alloc_length += 2;
if (alloc_length < len)
{
len = alloc_length;
}
}
else
{
memset(cdbufferb, 0, 34);
memset(cdbufferb, 1, 9);
cdbufferb[0] = 0;
cdbufferb[1] = 32;
cdbufferb[2] = 0xe; /* last session complete, disc finalized */
cdbufferb[7] = 0x20; /* unrestricted use */
cdbufferb[8] = 0x00; /* CD-ROM */
len=34;
}
cdrom_data_command_finish(id, len, len, max_len, 0);
break;
case GPCMD_READ_TRACK_INFORMATION:
max_len = cdb[7];
max_len <<= 8;
max_len |= cdb[8];
track = ((uint32_t) cdb[2]) << 24;
track |= ((uint32_t) cdb[3]) << 16;
track |= ((uint32_t) cdb[4]) << 8;
track |= (uint32_t) cdb[5];
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
return;
}
alloc_length = cdbufferb[0];
alloc_length <<= 8;
alloc_length |= cdbufferb[1];
alloc_length += 2;
if (alloc_length < len)
{
len = alloc_length;
}
}
else
{
if (((cdb[1] & 0x03) != 1) || (track != 1))
{
cdrom_invalid_field(id);
return;
}
len = 36;
memset(cdbufferb, 0, 36);
cdbufferb[0] = 0;
cdbufferb[1] = 34;
cdbufferb[2] = 1; /* track number (LSB) */
cdbufferb[3] = 1; /* session number (LSB) */
cdbufferb[5] = (0 << 5) | (0 << 4) | (4 << 0); /* not damaged, primary copy, data track */
cdbufferb[6] = (0 << 7) | (0 << 6) | (0 << 5) | (0 << 6) | (1 << 0); /* not reserved track, not blank, not packet writing, not fixed packet, data mode 1 */
cdbufferb[7] = (0 << 1) | (0 << 0); /* last recorded address not valid, next recordable address not valid */
cdbufferb[24] = (cdrom_drives[id].handler->size(id) >> 24) & 0xff; /* track size */
cdbufferb[25] = (cdrom_drives[id].handler->size(id) >> 16) & 0xff; /* track size */
cdbufferb[26] = (cdrom_drives[id].handler->size(id) >> 8) & 0xff; /* track size */
cdbufferb[27] = cdrom_drives[id].handler->size(id) & 0xff; /* track size */
if (len > max_len)
{
len = max_len;
cdbufferb[0] = ((max_len - 2) >> 8) & 0xff;
cdbufferb[1] = (max_len - 2) & 0xff;
}
}
cdrom_data_command_finish(id, len, len, max_len, 0);
break;
case GPCMD_PLAY_AUDIO_10:
case GPCMD_PLAY_AUDIO_12:
case GPCMD_PLAY_AUDIO_MSF:
case GPCMD_PLAY_AUDIO_TRACK_INDEX:
switch(cdb[0])
{
case GPCMD_PLAY_AUDIO_10:
msf = 0;
pos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
len = (cdb[7] << 8) | cdb[8];
break;
case GPCMD_PLAY_AUDIO_12:
msf = 0;
pos = (cdb[2] << 24) | (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
len = (cdb[6] << 24) | (cdb[7] << 16) | (cdb[8] << 8) | cdb[9];
break;
case GPCMD_PLAY_AUDIO_MSF:
/* This is apparently deprecated in the ATAPI spec, and apparently
has been since 1995 (!). Hence I'm having to guess most of it. */
msf = 1;
pos = (cdb[3] << 16) | (cdb[4] << 8) | cdb[5];
len = (cdb[6] << 16) | (cdb[7] << 8) | cdb[8];
break;
case GPCMD_PLAY_AUDIO_TRACK_INDEX:
msf = 2;
pos = (cdb[4] << 8) | cdb[5];
len = (cdb[7] << 8) | cdb[8];
break;
}
if ((cdrom_drive < 1) || (cdrom[id].cd_status <= CD_STATUS_DATA_ONLY) || !cdrom_drives[id].handler->is_track_audio(id, pos, msf))
{
cdrom_illegal_mode(id);
break;
}
if (cdrom_drives[id].handler->playaudio)
{
cdrom_drives[id].handler->playaudio(id, pos, len, msf);
}
else
{
cdrom_illegal_mode(id);
break;
}
cdrom_command_complete(id);
break;
case GPCMD_READ_SUBCHANNEL:
max_len = cdb[7];
max_len <<= 8;
max_len |= cdb[8];
msf = (cdb[1] >> 1) & 1;
cdrom_log("CD-ROM %i: Getting page %i (%s)\n", id, cdb[3], msf ? "MSF" : "LBA");
if ((cdrom_drives[id].handler->pass_through) && (cdb[3] != 1))
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
return;
}
switch(cdrom[id].cd_status)
{
case CD_STATUS_PLAYING:
cdbufferb[1] = 0x11;
break;
case CD_STATUS_PAUSED:
cdbufferb[1] = 0x12;
break;
case CD_STATUS_DATA_ONLY:
cdbufferb[1] = 0x15;
break;
default:
cdbufferb[1] = 0x13;
break;
}
switch(cdb[3])
{
case 0:
alloc_length = 4;
break;
case 1:
alloc_length = 16;
break;
default:
alloc_length = 24;
break;
}
if (!(cdb[2] & 0x40) || (cdb[3] == 0))
{
len = 4;
}
else
{
len = alloc_length;
}
}
else
{
if (cdb[3] > 3)
{
/* cdrom_log("CD-ROM %i: Read subchannel check condition %02X\n", id, cdb[3]); */
cdrom_invalid_field(id);
return;
}
switch(cdb[3])
{
case 0:
alloc_length = 4;
break;
case 1:
alloc_length = 16;
break;
default:
alloc_length = 24;
break;
}
memset(cdbufferb, 0, 24);
pos = 0;
cdbufferb[pos++] = 0;
cdbufferb[pos++] = 0; /*Audio status*/
cdbufferb[pos++] = 0; cdbufferb[pos++] = 0; /*Subchannel length*/
cdbufferb[pos++] = cdb[3] & 3; /*Format code*/
if (cdb[3] == 1)
{
cdbufferb[1] = cdrom_drives[id].handler->getcurrentsubchannel(id, &cdbufferb[5], msf);
switch(cdrom[id].cd_status)
{
case CD_STATUS_PLAYING:
cdbufferb[1] = 0x11;
break;
case CD_STATUS_PAUSED:
cdbufferb[1] = 0x12;
break;
case CD_STATUS_DATA_ONLY:
cdbufferb[1] = 0x15;
break;
default:
cdbufferb[1] = 0x13;
break;
}
}
if (!(cdb[2] & 0x40) || (cdb[3] == 0))
{
len = 4;
}
else
{
len = alloc_length;
}
}
cdrom_data_command_finish(id, len, len, max_len, 0);
break;
case GPCMD_READ_DVD_STRUCTURE:
if (cdrom_drives[id].handler->pass_through)
{
ret = cdrom_pass_through(id, &len, cdrom[id].current_cdb, cdbufferb);
if (!ret)
{
return;
}
}
else
{
len = (((uint32_t) cdb[6]) << 24) | (((uint32_t) cdb[7]) << 16) | (((uint32_t) cdb[8]) << 8) | ((uint32_t) cdb[9]);
alloc_length = len;
if (cdb[7] < 0xff)
{
if (len <= CD_MAX_SECTORS)
{
cdrom_incompatible_format(id);
return;
}
else
{
cdrom_invalid_field(id);
return;
}
}
memset(cdbufferb, 0, (alloc_length > 256 * 512 + 4) ? (256 * 512 + 4) : alloc_length);
switch (cdb[7])
{
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x06: case 0x07:
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17:
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f:
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: case 0x3f:
case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
case 0x48: case 0x49: case 0x4a: case 0x4b: case 0x4c: case 0x4d: case 0x4e: case 0x4f:
case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
case 0x58: case 0x59: case 0x5a: case 0x5b: case 0x5c: case 0x5d: case 0x5e: case 0x5f:
case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f:
case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f:
case 0xff:
if (cdb[1] == 0)
{
ret = cdrom_read_dvd_structure(id, format, cdb, cdbufferb);
if (ret)
{
cdrom_data_command_finish(id, len, len, alloc_length, 0);
}
return;
}
/* TODO: BD support, fall through for now */
/* Generic disk structures */
case 0x80: /* TODO: AACS volume identifier */
case 0x81: /* TODO: AACS media serial number */
case 0x82: /* TODO: AACS media identifier */
case 0x83: /* TODO: AACS media key block */
case 0x90: /* TODO: List of recognized format layers */
case 0xc0: /* TODO: Write protection status */
default:
cdrom_invalid_field(id);
return;
}
}
break;
case GPCMD_START_STOP_UNIT:
switch(cdb[4] & 3)
{
case 0: /* Stop the disc. */
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
break;
case 1: /* Start the disc and read the TOC. */
cdrom_drives[id].handler->medium_changed(id); /* This causes a TOC reload. */
break;
case 2: /* Eject the disc if possible. */
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
cdrom_eject(id);
break;
case 3: /* Load the disc (close tray). */
cdrom_reload(id);
break;
}
cdrom_command_complete(id);
break;
case GPCMD_INQUIRY:
max_len = cdb[3];
max_len <<= 8;
max_len |= cdb[4];
if (cdb[1] & 1)
{
preamble_len = 4;
size_idx = 3;
cdbufferb[idx++] = 05;
cdbufferb[idx++] = cdb[2];
cdbufferb[idx++] = 0;
idx++;
switch (cdb[2])
{
case 0x00:
cdbufferb[idx++] = 0x00;
cdbufferb[idx++] = 0x83;
break;
case 0x83:
if (idx + 24 > max_len)
{
cdrom_data_phase_error(id);
return;
}
cdbufferb[idx++] = 0x02;
cdbufferb[idx++] = 0x00;
cdbufferb[idx++] = 0x00;
cdbufferb[idx++] = 20;
ide_padstr8(cdbufferb + idx, 20, "53R141"); /* Serial */
idx += 20;
if (idx + 72 > cdb[4])
{
goto atapi_out;
}
cdbufferb[idx++] = 0x02;
cdbufferb[idx++] = 0x01;
cdbufferb[idx++] = 0x00;
cdbufferb[idx++] = 68;
ide_padstr8(cdbufferb + idx, 8, EMU_NAME); /* Vendor */
idx += 8;
ide_padstr8(cdbufferb + idx, 40, device_identify_ex); /* Product */
idx += 40;
ide_padstr8(cdbufferb + idx, 20, "53R141"); /* Product */
idx += 20;
break;
default:
cdrom_log("INQUIRY: Invalid page: %02X\n", cdb[2]);
cdrom_invalid_field(id);
return;
}
}
else
{
preamble_len = 5;
size_idx = 4;
memset(cdbufferb, 0, 8);
cdbufferb[0] = 5; /*CD-ROM*/
cdbufferb[1] = 0x80; /*Removable*/
cdbufferb[2] = (cdrom_drives[id].bus_type == CDROM_BUS_SCSI) ? 0x02 : 0x00; /*SCSI-2 compliant*/
cdbufferb[3] = (cdrom_drives[id].bus_type == CDROM_BUS_SCSI) ? 0x02 : 0x21;
cdbufferb[4] = 31;
ide_padstr8(cdbufferb + 8, 8, EMU_NAME); /* Vendor */
ide_padstr8(cdbufferb + 16, 16, device_identify); /* Product */
ide_padstr8(cdbufferb + 32, 4, EMU_VERSION); /* Revision */
idx = 36;
}
atapi_out:
cdbufferb[size_idx] = idx - preamble_len;
len=idx;
cdrom_data_command_finish(id, len, len, max_len, 0);
break;
case GPCMD_PREVENT_REMOVAL:
cdrom_command_complete(id);
break;
case GPCMD_PAUSE_RESUME_ALT:
case GPCMD_PAUSE_RESUME:
if (cdb[8] & 1)
{
if (cdrom_drives[id].handler->resume)
{
cdrom_drives[id].handler->resume(id);
}
else
{
cdrom_illegal_mode(id);
break;
}
}
else
{
if (cdrom_drives[id].handler->pause)
{
cdrom_drives[id].handler->pause(id);
}
else
{
cdrom_illegal_mode(id);
break;
}
}
cdrom_command_complete(id);
break;
case GPCMD_SEEK_6:
case GPCMD_SEEK_10:
switch(cdb[0])
{
case GPCMD_SEEK_6:
pos = (cdb[2] << 8) | cdb[3];
break;
case GPCMD_SEEK_10:
pos = (cdb[2] << 24) | (cdb[3]<<16) | (cdb[4]<<8) | cdb[5];
break;
}
cdrom_seek(id, pos);
cdrom_command_complete(id);
break;
case GPCMD_READ_CDROM_CAPACITY:
if (cdrom_read_capacity(id, cdrom[id].current_cdb, cdbufferb, &len) == 0)
{
return;
}
cdrom_data_command_finish(id, len, len, len, 0);
break;
case GPCMD_STOP_PLAY_SCAN:
if (cdrom_drives[id].handler->stop)
{
cdrom_drives[id].handler->stop(id);
}
else
{
cdrom_illegal_mode(id);
break;
}
cdrom_command_complete(id);
break;
default:
cdrom_illegal_opcode(id);
break;
}
/* cdrom_log("CD-ROM %i: Phase: %02X, request length: %i\n", cdrom[id].phase, cdrom[id].request_length); */
}
/* This is for block reads. */
int cdrom_block_check(uint8_t id)
{
uint32_t alloc_length = 0;
int ret = 0;
/* If this is a media access command, and we hit the end of the block but not the entire length,
read the next block. */
if (cdrom_is_media_access(id))
{
/* We have finished the current block. */
cdrom_log("CD-ROM %i: %i bytes total read, %i bytes all total\n", id, cdrom[id].total_read, cdrom[id].all_blocks_total);
if (cdrom[id].total_read >= cdrom[id].all_blocks_total)
{
cdrom_log("CD-ROM %i: %i bytes read, current block finished\n", id, cdrom[id].total_read);
/* Read the next block. */
ret = cdrom_read_blocks(id, &alloc_length, 0);
if (ret == -1)
{
/* Return value is -1 - there are no further blocks to read. */
cdrom_log("CD-ROM %i: %i bytes read, no further blocks to read\n", id, cdrom[id].total_read);
cdrom[id].status = BUSY_STAT;
return 1;
}
else if (ret == 0)
{
/* Return value is 0 - an error has occurred. */
cdrom_log("CD-ROM %i: %i bytes read, error while reading blocks\n", id, cdrom[id].total_read);
cdrom[id].status = BUSY_STAT | (cdrom[id].status & ERR_STAT);
return 0;
}
else
{
/* Return value is 1 - sectors have been read successfully. */
cdrom[id].pos = 0;
cdrom[id].all_blocks_total += cdrom[id].block_total;
cdrom_log("CD-ROM %i: %i bytes read, next block(s) read successfully, %i bytes are still left\n", id, cdrom[id].total_read, cdrom[id].all_blocks_total - cdrom[id].total_read);
return 1;
}
}
else
{
/* Blocks not exhausted, tell the host to check for buffer length. */
cdrom_log("CD-ROM %i: Blocks not yet finished\n", id);
return 1;
}
}
else
{
/* Not a media access command, ALWAYS do the callback. */
cdrom_log("CD-ROM %i: Not a media access command\n", id);
return 1;
}
}
/* This is the general ATAPI callback. */
void cdrom_callback(uint8_t id) /* Callback for non-Read CD commands */
{
int old_pos = 0;
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
{
cdrom_log("CD-ROM %i: Lowering IDE IRQ\n", id);
ide_irq_lower(&(ide_drives[cdrom_drives[id].ide_channel]));
}
cdrom[id].status = BUSY_STAT;
if (cdrom[id].total_read >= cdrom[id].packet_len)
{
cdrom_log("CD-ROM %i: %i bytes read, command done\n", id, cdrom[id].total_read);
cdrom[id].pos = cdrom[id].request_pos = 0;
cdrom_command_complete(id);
}
else
{
cdrom_log("CD-ROM %i: %i bytes read, %i bytes are still left\n", id, cdrom[id].total_read, cdrom[id].packet_len - cdrom[id].total_read);
/* Make sure to keep pos, and reset request_pos to 0. */
/* Also make sure to not reset total_read. */
old_pos = cdrom[id].pos;
cdrom[id].packet_status = CDROM_PHASE_DATA_IN;
cdrom_command_common(id);
cdrom[id].pos = old_pos;
cdrom[id].request_pos = 0;
}
}
/* 0 = Continue transfer; 1 = Continue transfer, IRQ; -1 = Terminate transfer; -2 = Terminate transfer with error */
int cdrom_mode_select_return(uint8_t id, int ret)
{
switch(ret)
{
case 0:
/* Invalid field in parameter list. */
case -6:
/* Attempted to write to a non-existent CD-ROM drive (should never occur, but you never know). */
cdrom_invalid_field_pl(id);
return -2;
case 1:
/* Successful, more data needed. */
if (cdrom[id].pos >= (cdrom[id].packet_len + 2))
{
cdrom[id].pos = 0;
cdrom_command_write(id);
return 1;
}
return 0;
case 2:
/* Successful, more data needed, second byte not yet processed. */
return 0;
case -3:
/* Not initialized. */
case -4:
/* Unknown phase. */
cdrom_illegal_opcode(id);
return -2;
case -5:
/* Command terminated successfully. */
/* cdrom_command_complete(id); */
return -1;
default:
return -15;
}
}
void cdrom_phase_callback(uint8_t id);
int cdrom_read_from_ide_dma(uint8_t channel)
{
uint8_t *cdbufferb;
uint8_t id = atapi_cdrom_drives[channel];
cdbufferb = (uint8_t *) cdrom[id].buffer;
if (id > CDROM_NUM)
{
return 0;
}
if (ide_bus_master_write)
{
if (ide_bus_master_write(channel >> 1, cdbufferb, cdrom[id].request_length))
{
cdrom_data_phase_error(id);
cdrom_phase_callback(id);
return 0;
}
else
{
return 1;
}
}
return 0;
}
int cdrom_read_from_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t *cdbufferb;
uint8_t id = scsi_cdrom_drives[scsi_id][scsi_lun];
cdbufferb = (uint8_t *) cdrom[id].buffer;
if (id > CDROM_NUM)
{
return 0;
}
cdrom_log("Reading from SCSI DMA: SCSI ID %02X, init length %i\n", scsi_id, SCSIDevices[scsi_id][scsi_lun].InitLength);
memcpy(cdbufferb, SCSIDevices[scsi_id][scsi_lun].CmdBuffer, SCSIDevices[scsi_id][scsi_lun].InitLength);
return 1;
}
int cdrom_read_from_dma(uint8_t id)
{
uint8_t *cdbufferb = (uint8_t *) cdrom[id].buffer;
int i = 0;
int ret = 0;
int in_data_length = 0;
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
ret = cdrom_read_from_scsi_dma(cdrom_drives[id].scsi_device_id, cdrom_drives[id].scsi_device_lun);
}
else
{
ret = cdrom_read_from_ide_dma(cdrom_drives[id].ide_channel);
}
if (!ret)
{
return 0;
}
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
in_data_length = SCSIDevices[cdrom_drives[id].scsi_device_id][cdrom_drives[id].scsi_device_lun].InitLength;
cdrom_log("CD-ROM %i: SCSI Input data length: %i\n", id, in_data_length);
}
else
{
in_data_length = cdrom[id].request_length;
cdrom_log("CD-ROM %i: ATAPI Input data length: %i\n", id, in_data_length);
}
for (i = 0; i < in_data_length; i++)
{
ret = cdrom_mode_select_write(id, cdbufferb[i]);
ret = cdrom_mode_select_return(id, ret);
if (ret == -1)
{
return 1;
}
else if (ret == -2)
{
cdrom_phase_callback(id);
return 0;
}
}
return 0;
}
int cdrom_write_to_ide_dma(uint8_t channel)
{
uint8_t *cdbufferb;
uint8_t id = atapi_cdrom_drives[channel];
2017-02-09 01:41:32 +01:00
int transfer_length = 0;
int cdbufferb_pos = 0;
int bus_master_len = 0;
int ret = 0;
if (id > CDROM_NUM)
{
return 0;
}
cdbufferb = (uint8_t *) cdrom[id].buffer;
2017-02-09 01:41:32 +01:00
transfer_length = cdrom[id].init_length;
if (ide_bus_master_read)
{
2017-02-09 01:41:32 +01:00
while(transfer_length > 0)
{
/* pclog("CD-ROM %i: ATAPI DMA on position: %08X...\n", id, cdbufferb + cdbufferb_pos); */
2017-02-09 01:41:32 +01:00
bus_master_len = piix_bus_master_get_count(channel >> 1);
ret = piix_bus_master_dma_read_ex(channel >> 1, cdbufferb + cdbufferb_pos);
if (ret != 0)
{
break;
}
transfer_length -= bus_master_len;
cdbufferb_pos += bus_master_len;
}
if (ret > 0)
{
/* pclog("CD-ROM %i: ATAPI DMA error\n", id); */
cdrom_data_phase_error(id);
cdrom_phase_callback(id);
return 0;
}
else
{
/* pclog("CD-ROM %i: ATAPI DMA successful\n", id); */
return 1;
}
}
return 0;
}
int cdrom_write_to_scsi_dma(uint8_t scsi_id, uint8_t scsi_lun)
{
uint8_t *cdbufferb;
uint8_t id = scsi_cdrom_drives[scsi_id][scsi_lun];
if (id > CDROM_NUM)
{
return 0;
}
cdbufferb = (uint8_t *) cdrom[id].buffer;
cdrom_log("Writing to SCSI DMA: SCSI ID %02X, init length %i\n", scsi_id, SCSIDevices[scsi_id][scsi_lun].InitLength);
memcpy(SCSIDevices[scsi_id][scsi_lun].CmdBuffer, cdbufferb, SCSIDevices[scsi_id][scsi_lun].InitLength);
cdrom_log("CD-ROM %i: Data from CD buffer: %02X %02X %02X %02X %02X %02X %02X %02X\n", id, cdbufferb[0], cdbufferb[1], cdbufferb[2], cdbufferb[3], cdbufferb[4], cdbufferb[5], cdbufferb[6], cdbufferb[7]);
cdrom_log("CD-ROM %i: Data from SCSI DMA : %02X %02X %02X %02X %02X %02X %02X %02X\n", id, SCSIDevices[scsi_id][scsi_lun].CmdBuffer[0], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[1], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[2], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[3], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[4], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[5], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[6], SCSIDevices[scsi_id][scsi_lun].CmdBuffer[7]);
return 1;
}
int cdrom_write_to_dma(uint8_t id)
{
int ret = 0;
if (cdrom_drives[id].bus_type == CDROM_BUS_SCSI)
{
ret = cdrom_write_to_scsi_dma(cdrom_drives[id].scsi_device_id, cdrom_drives[id].scsi_device_lun);
}
else
{
ret = cdrom_write_to_ide_dma(cdrom_drives[id].ide_channel);
}
if (!ret)
{
return 0;
}
return 1;
}
void cdrom_irq_raise(uint8_t id)
{
if (cdrom_drives[id].bus_type < CDROM_BUS_SCSI)
{
ide_irq_raise(&(ide_drives[cdrom_drives[id].ide_channel]));
}
}
/* If the result is 1, issue an IRQ, otherwise not. */
void cdrom_phase_callback(uint8_t id)
{
switch(cdrom[id].packet_status)
{
case CDROM_PHASE_IDLE:
cdrom_log("CD-ROM %i: CDROM_PHASE_IDLE\n", id);
cdrom[id].pos=0;
cdrom[id].phase = 1;
cdrom[id].status = READY_STAT | DRQ_STAT | (cdrom[id].status & ERR_STAT);
return;
case CDROM_PHASE_COMMAND:
cdrom_log("CD-ROM %i: CDROM_PHASE_COMMAND\n", id);
cdrom[id].status = BUSY_STAT | (cdrom[id].status &ERR_STAT);
memcpy(cdrom[id].atapi_cdb, (uint8_t *) cdrom[id].buffer, cdrom[id].cdb_len);
cdrom_command(id, cdrom[id].atapi_cdb);
return;
case CDROM_PHASE_COMPLETE:
cdrom_log("CD-ROM %i: CDROM_PHASE_COMPLETE\n", id);
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
cdrom[id].packet_status = 0xFF;
update_status_bar_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return;
case CDROM_PHASE_DATA_OUT:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_OUT\n", id);
cdrom[id].status = READY_STAT | DRQ_STAT | (cdrom[id].status & ERR_STAT);
cdrom[id].phase = 0;
cdrom_irq_raise(id);
return;
case CDROM_PHASE_DATA_OUT_DMA:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_OUT_DMA\n", id);
cdrom_read_from_dma(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
update_status_bar_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return;
case CDROM_PHASE_DATA_IN:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_IN\n", id);
cdrom[id].status = READY_STAT | DRQ_STAT | (cdrom[id].status & ERR_STAT);
cdrom[id].phase = 2;
cdrom_irq_raise(id);
return;
case CDROM_PHASE_DATA_IN_DMA:
cdrom_log("CD-ROM %i: CDROM_PHASE_DATA_IN_DMA\n", id);
cdrom_write_to_dma(id);
cdrom[id].packet_status = CDROM_PHASE_COMPLETE;
cdrom[id].status = READY_STAT;
cdrom[id].phase = 3;
update_status_bar_icon(SB_CDROM | id, 0);
cdrom_irq_raise(id);
return;
case CDROM_PHASE_ERROR:
cdrom_log("CD-ROM %i: CDROM_PHASE_ERROR\n", id);
cdrom[id].status = READY_STAT | ERR_STAT;
cdrom[id].phase = 3;
cdrom_irq_raise(id);
return;
}
}
/* Reimplement as 8-bit due to reimplementation of IDE data read and write. */
uint32_t cdrom_read(uint8_t channel, int length)
{
uint8_t *cdbufferb;
uint16_t *cdbufferw;
uint32_t *cdbufferl;
uint8_t id = atapi_cdrom_drives[channel];
uint32_t temp = 0;
int ret = 0;
if (id > CDROM_NUM)
{
return 0;
}
cdbufferb = (uint8_t *) cdrom[id].buffer;
cdbufferw = cdrom[id].buffer;
cdbufferl = (uint32_t *) cdrom[id].buffer;
switch(length)
{
case 1:
temp = cdbufferb[cdrom[id].pos];
cdrom[id].pos++;
cdrom[id].request_pos++;
break;
case 2:
temp = cdbufferw[cdrom[id].pos >> 1];
cdrom[id].pos += 2;
cdrom[id].request_pos += 2;
break;
case 4:
temp = cdbufferl[cdrom[id].pos >> 2];
cdrom[id].pos += 4;
cdrom[id].request_pos += 4;
break;
default:
return 0;
}
if (cdrom[id].packet_status == CDROM_PHASE_DATA_IN)
{
cdrom[id].total_read += length;
ret = cdrom_block_check(id);
/* If the block check has returned 0, this means all the requested blocks have been read, therefore the command has finished. */
if (ret)
{
cdrom_log("CD-ROM %i: Return value is 1 (request length: %i)\n", id, cdrom[id].request_length);
if (cdrom[id].request_pos >= cdrom[id].request_length)
{
/* Time for a DRQ. */
cdrom_log("CD-ROM %i: Issuing read callback\n", id);
cdrom_callback(id);
}
else
{
cdrom_log("CD-ROM %i: Doing nothing\n", id);
}
}
else
{
cdrom_log("CD-ROM %i: Return value is 0\n", id);
}
cdrom_log("CD-ROM %i: Returning: %02X (buffer position: %i, request position: %i, total: %i)\n", id, temp, cdrom[id].pos, cdrom[id].request_pos, cdrom[id].total_read);
return temp;
}
else
{
cdrom_log("CD-ROM %i: Returning zero (buffer position: %i, request position: %i, total: %i)\n", id, cdrom[id].pos, cdrom[id].request_pos, cdrom[id].total_read);
return 0;
}
}
/* Reimplement as 8-bit due to reimplementation of IDE data read and write. */
void cdrom_write(uint8_t channel, uint32_t val, int length)
{
uint8_t i = 0;
uint8_t *cdbufferb;
uint16_t *cdbufferw;
uint32_t *cdbufferl;
uint8_t old_pos = 0;
uint8_t id = atapi_cdrom_drives[channel];
int ret = 0;
if (id > CDROM_NUM)
{
2017-01-17 00:04:49 +01:00
return;
}
cdbufferb = (uint8_t *) cdrom[id].buffer;
cdbufferw = cdrom[id].buffer;
cdbufferl = (uint32_t *) cdrom[id].buffer;
old_pos = cdrom[id].pos;
switch(length)
{
case 1:
cdbufferb[cdrom[id].pos] = val & 0xff;
cdrom[id].pos++;
break;
case 2:
cdbufferw[cdrom[id].pos >> 1] = val & 0xffff;
cdrom[id].pos += 2;
break;
case 4:
cdbufferl[cdrom[id].pos >> 2] = val;
cdrom[id].pos += 4;
break;
default:
return;
}
if (cdrom[id].packet_status == CDROM_PHASE_DATA_OUT)
{
for (i = 0; i < length; i++)
{
ret = cdrom_mode_select_write(id, cdbufferb[old_pos + i]);
cdrom_mode_select_return(id, ret);
}
return;
}
else if (cdrom[id].packet_status == CDROM_PHASE_IDLE)
{
if (cdrom[id].pos >= cdrom[id].cdb_len)
{
cdrom[id].pos=0;
cdrom[id].status = BUSY_STAT;
cdrom[id].packet_status = CDROM_PHASE_COMMAND;
timer_process();
cdrom_phase_callback(id);
timer_update_outstanding();
}
2017-01-17 00:04:49 +01:00
return;
}
}