mirror of https://github.com/inolen/redream.git
refactored gdrom state machine to have more formalized transitions
This commit is contained in:
parent
525dabd4c5
commit
e3e7975a6c
|
@ -19,66 +19,74 @@
|
|||
|
||||
/* internal gdrom state machine */
|
||||
enum gd_event {
|
||||
EV_ATA_CMD_DONE,
|
||||
/* each incomming SPI command will either:
|
||||
a.) have no additional data and immediately fire EV_SPI_CMD_DONE
|
||||
b.) read additional data over PIO with EV_SPI_READ_START
|
||||
c.) write additional data over PIO with EV_SPI_WRITE_START
|
||||
d.) write additional data over DMA / PIO with EV_SPI_WRITE_SECTORS */
|
||||
EV_SPI_WAIT_CMD,
|
||||
EV_SPI_READ_START,
|
||||
EV_SPI_READ_END,
|
||||
EV_SPI_WRITE_START,
|
||||
EV_SPI_WRITE_SECTORS,
|
||||
EV_SPI_WRITE_END,
|
||||
EV_SPI_CMD_DONE,
|
||||
EVENT_ATA_CMD,
|
||||
EVENT_PIO_WRITE,
|
||||
EVENT_SPI_CMD,
|
||||
EVENT_PIO_READ,
|
||||
EVENT_SPI_DATA,
|
||||
MAX_EVENTS,
|
||||
};
|
||||
|
||||
enum gd_state {
|
||||
STATE_STANDBY,
|
||||
STATE_SPI_READ_CMD,
|
||||
STATE_SPI_READ_DATA,
|
||||
STATE_SPI_WRITE_DATA,
|
||||
STATE_SPI_WRITE_SECTORS,
|
||||
STATE_READ_ATA_CMD,
|
||||
STATE_READ_ATA_DATA,
|
||||
STATE_READ_SPI_DATA,
|
||||
STATE_WRITE_SPI_DATA,
|
||||
MAX_STATES,
|
||||
};
|
||||
|
||||
struct cdread {
|
||||
int dma;
|
||||
enum gd_secfmt sector_fmt;
|
||||
enum gd_secmask sector_mask;
|
||||
int first_sector;
|
||||
int num_sectors;
|
||||
typedef void (*gd_event_cb)(struct gdrom *, int);
|
||||
|
||||
static void gdrom_event(struct gdrom *gd, enum gd_event ev, int arg);
|
||||
static void gdrom_ata_cmd(struct gdrom *gd, int arg);
|
||||
static void gdrom_pio_write(struct gdrom *gd, int arg);
|
||||
static void gdrom_spi_cmd(struct gdrom *gd, int arg);
|
||||
static void gdrom_pio_read(struct gdrom *gd, int arg);
|
||||
static void gdrom_spi_data(struct gdrom *gd, int arg);
|
||||
|
||||
/* clang-format off */
|
||||
gd_event_cb gd_transitions[MAX_STATES][MAX_EVENTS] = {
|
||||
{ &gdrom_ata_cmd, NULL, NULL, NULL, NULL, },
|
||||
{ NULL, &gdrom_pio_write, &gdrom_spi_cmd, NULL, NULL, },
|
||||
{ NULL, &gdrom_pio_write, NULL, NULL, &gdrom_spi_data, },
|
||||
{ NULL, NULL, NULL, &gdrom_pio_read, NULL, },
|
||||
};
|
||||
/* clang-format on */
|
||||
|
||||
struct gdrom {
|
||||
struct device;
|
||||
|
||||
/* hardware information accessed at runtime through REQ_MODE and SET_MODE */
|
||||
struct gd_hw_info hw_info;
|
||||
|
||||
enum gd_state state;
|
||||
struct gd_hw_info hw_info;
|
||||
struct disc *disc;
|
||||
|
||||
/* internal registers */
|
||||
union gd_error error;
|
||||
union gd_features features;
|
||||
union gd_intreason ireason;
|
||||
union gd_sectnum sectnum;
|
||||
union gd_bytect byte_count;
|
||||
union gd_status status;
|
||||
/* current CD_READ state */
|
||||
struct cdread req;
|
||||
|
||||
/* cdread state */
|
||||
int cdr_dma;
|
||||
enum gd_secfmt cdr_secfmt;
|
||||
enum gd_secmask cdr_secmask;
|
||||
int cdr_first_sector;
|
||||
int cdr_num_sectors;
|
||||
|
||||
/* pio state */
|
||||
uint8_t pio_buffer[0x10000];
|
||||
int pio_head;
|
||||
int pio_size;
|
||||
int pio_read;
|
||||
int pio_offset;
|
||||
|
||||
/* dma state */
|
||||
uint8_t dma_buffer[8192 * SECTOR_SIZE];
|
||||
uint8_t dma_buffer[0x10000];
|
||||
int dma_head;
|
||||
int dma_size;
|
||||
};
|
||||
|
||||
static void gdrom_event(struct gdrom *gd, enum gd_event ev, intptr_t arg0,
|
||||
intptr_t arg1);
|
||||
|
||||
static int gdrom_get_fad(uint8_t a, uint8_t b, uint8_t c, int msf) {
|
||||
if (msf) {
|
||||
/* MSF mode
|
||||
|
@ -95,11 +103,320 @@ static int gdrom_get_fad(uint8_t a, uint8_t b, uint8_t c, int msf) {
|
|||
return (a << 16) | (b << 8) | c;
|
||||
}
|
||||
|
||||
static void gdrom_set_mode(struct gdrom *gd, int offset, uint8_t *data,
|
||||
int data_size) {
|
||||
memcpy((void *)&gd->hw_info + offset, data, data_size);
|
||||
static void gdrom_spi_end(struct gdrom *gd) {
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 1;
|
||||
gd->status.DRDY = 1;
|
||||
gd->status.BSY = 0;
|
||||
gd->status.DRQ = 0;
|
||||
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_READ_ATA_CMD;
|
||||
}
|
||||
|
||||
static void gdrom_spi_cdread(struct gdrom *gd) {
|
||||
if (gd->cdr_dma) {
|
||||
int max_dma_sectors = sizeof(gd->dma_buffer) / SECTOR_SIZE;
|
||||
|
||||
/* fill DMA buffer with as many sectors as possible */
|
||||
int num_sectors = MIN(gd->cdr_num_sectors, max_dma_sectors);
|
||||
gd->dma_size = gdrom_read_sectors(gd, gd->cdr_first_sector, gd->cdr_secfmt,
|
||||
gd->cdr_secmask, num_sectors,
|
||||
gd->dma_buffer, sizeof(gd->dma_buffer));
|
||||
gd->dma_head = 0;
|
||||
|
||||
/* update sector read state */
|
||||
gd->cdr_first_sector += num_sectors;
|
||||
gd->cdr_num_sectors -= num_sectors;
|
||||
|
||||
/* gdrom state won't be updated until DMA transfer is completed */
|
||||
} else {
|
||||
int max_pio_sectors = sizeof(gd->pio_buffer) / SECTOR_SIZE;
|
||||
|
||||
/* fill PIO buffer with as many sectors as possible */
|
||||
int num_sectors = MIN(gd->cdr_num_sectors, max_pio_sectors);
|
||||
gd->pio_size = gdrom_read_sectors(
|
||||
gd, gd->cdr_first_sector, gd->cdr_secfmt, gd->cdr_secmask, num_sectors,
|
||||
gd->pio_buffer, (int)sizeof(gd->pio_buffer));
|
||||
gd->pio_head = 0;
|
||||
|
||||
/* update sector read state */
|
||||
gd->cdr_first_sector += num_sectors;
|
||||
gd->cdr_num_sectors -= num_sectors;
|
||||
|
||||
/* update gdrom state */
|
||||
gd->byte_count.full = gd->pio_size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_spi_read(struct gdrom *gd, int offset, int size) {
|
||||
gd->cdr_num_sectors = 0;
|
||||
|
||||
gd->pio_head = 0;
|
||||
gd->pio_size = size;
|
||||
gd->pio_offset = offset;
|
||||
|
||||
gd->byte_count.full = size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_READ_SPI_DATA;
|
||||
}
|
||||
|
||||
static void gdrom_spi_write(struct gdrom *gd, void *data, int size) {
|
||||
gd->cdr_num_sectors = 0;
|
||||
|
||||
CHECK(size < (int)sizeof(gd->pio_buffer));
|
||||
memcpy(gd->pio_buffer, data, size);
|
||||
gd->pio_size = size;
|
||||
gd->pio_head = 0;
|
||||
|
||||
gd->byte_count.full = gd->pio_size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_WRITE_SPI_DATA;
|
||||
}
|
||||
|
||||
static void gdrom_ata_end(struct gdrom *gd) {
|
||||
gd->status.DRDY = 1;
|
||||
gd->status.BSY = 0;
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_READ_ATA_CMD;
|
||||
}
|
||||
|
||||
static void gdrom_spi_data(struct gdrom *gd, int arg) {
|
||||
/* only used by SET_MODE */
|
||||
int offset = gd->pio_offset;
|
||||
uint8_t *data = gd->pio_buffer;
|
||||
int size = gd->pio_size;
|
||||
memcpy((void *)&gd->hw_info + offset, data, size);
|
||||
|
||||
gdrom_spi_end(gd);
|
||||
}
|
||||
|
||||
static void gdrom_pio_read(struct gdrom *gd, int arg) {
|
||||
if (gd->pio_head == gd->pio_size) {
|
||||
if (gd->cdr_num_sectors) {
|
||||
gdrom_spi_cdread(gd);
|
||||
} else {
|
||||
gdrom_spi_end(gd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_spi_cmd(struct gdrom *gd, int arg) {
|
||||
uint8_t *data = gd->pio_buffer;
|
||||
enum gd_spi_cmd cmd = (enum gd_spi_cmd)data[0];
|
||||
|
||||
LOG_GDROM("gdrom_spi_cmd 0x%x", cmd);
|
||||
|
||||
gd->status.DRQ = 0;
|
||||
gd->status.BSY = 1;
|
||||
|
||||
switch (cmd) {
|
||||
/*
|
||||
* packet command flow for pio data to host
|
||||
*/
|
||||
case SPI_REQ_STAT: {
|
||||
int offset = data[2];
|
||||
int size = data[4];
|
||||
|
||||
uint8_t stat[10];
|
||||
stat[0] = gd->sectnum.status;
|
||||
stat[1] = gd->sectnum.format << 4;
|
||||
stat[2] = 0x4;
|
||||
stat[3] = 2;
|
||||
stat[4] = 0;
|
||||
stat[5] = 0;
|
||||
stat[6] = 0;
|
||||
stat[7] = 0;
|
||||
stat[8] = 0;
|
||||
stat[9] = 0;
|
||||
|
||||
gdrom_spi_write(gd, stat + offset, size);
|
||||
} break;
|
||||
|
||||
case SPI_REQ_MODE: {
|
||||
int offset = data[2];
|
||||
int size = data[4];
|
||||
|
||||
gdrom_spi_write(gd, (void *)&gd->hw_info + offset, size);
|
||||
} break;
|
||||
|
||||
/*case SPI_REQ_ERROR: {
|
||||
} break;*/
|
||||
|
||||
case SPI_GET_TOC: {
|
||||
enum gd_area area_type = (enum gd_area)(data[1] & 0x1);
|
||||
int size = (data[3] << 8) | data[4];
|
||||
|
||||
struct gd_toc toc;
|
||||
gdrom_get_toc(gd, area_type, &toc);
|
||||
|
||||
gdrom_spi_write(gd, &toc, size);
|
||||
} break;
|
||||
|
||||
case SPI_REQ_SES: {
|
||||
int session = data[2];
|
||||
int size = data[4];
|
||||
|
||||
struct gd_session ses;
|
||||
gdrom_get_session(gd, session, &ses);
|
||||
|
||||
gdrom_spi_write(gd, &ses, sizeof(ses));
|
||||
} break;
|
||||
|
||||
case SPI_GET_SCD: {
|
||||
int format = data[1] & 0xf;
|
||||
int size = (data[3] << 8) | data[4];
|
||||
|
||||
uint8_t scd[GD_MAX_SUBCODE_SIZE];
|
||||
gdrom_get_subcode(gd, format, scd);
|
||||
|
||||
gdrom_spi_write(gd, scd, size);
|
||||
} break;
|
||||
|
||||
case SPI_CD_READ: {
|
||||
int msf = (data[1] & 0x1);
|
||||
|
||||
gd->cdr_dma = gd->features.dma;
|
||||
gd->cdr_secfmt = (enum gd_secfmt)((data[1] & 0xe) >> 1);
|
||||
gd->cdr_secmask = (enum gd_secmask)((data[1] >> 4) & 0xff);
|
||||
gd->cdr_first_sector = gdrom_get_fad(data[2], data[3], data[4], msf);
|
||||
gd->cdr_num_sectors = (data[8] << 16) | (data[9] << 8) | data[10];
|
||||
|
||||
gdrom_spi_cdread(gd);
|
||||
} break;
|
||||
|
||||
/*case SPI_CD_READ2: {
|
||||
} break;*/
|
||||
|
||||
/*
|
||||
* packet command flow for pio data from host
|
||||
*/
|
||||
case SPI_SET_MODE: {
|
||||
int offset = data[2];
|
||||
int size = data[4];
|
||||
|
||||
gdrom_spi_read(gd, offset, size);
|
||||
} break;
|
||||
|
||||
/*
|
||||
* non-data command flow
|
||||
*/
|
||||
case SPI_TEST_UNIT: {
|
||||
gdrom_spi_end(gd);
|
||||
} break;
|
||||
|
||||
case SPI_CD_OPEN:
|
||||
case SPI_CD_PLAY:
|
||||
case SPI_CD_SEEK:
|
||||
case SPI_CD_SCAN: {
|
||||
gdrom_spi_end(gd);
|
||||
} break;
|
||||
|
||||
/* 0x70 and 0x71 appear to be a part of some kind of security check that has
|
||||
yet to be properly reverse engineered. be a lemming and reply / set state
|
||||
as expected by various games */
|
||||
case SPI_UNKNOWN_70: {
|
||||
gdrom_spi_end(gd);
|
||||
} break;
|
||||
|
||||
case SPI_UNKNOWN_71: {
|
||||
gd->sectnum.status = DST_PAUSE;
|
||||
gdrom_spi_write(gd, (uint8_t *)reply_71, sizeof(reply_71));
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("Unsupported SPI command %d", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_pio_write(struct gdrom *gd, int arg) {
|
||||
if (gd->state == STATE_READ_ATA_DATA && gd->pio_head == SPI_CMD_SIZE) {
|
||||
gdrom_event(gd, EVENT_SPI_CMD, 0);
|
||||
} else if (gd->state == STATE_READ_SPI_DATA && gd->pio_head == gd->pio_size) {
|
||||
gdrom_event(gd, EVENT_SPI_DATA, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_ata_cmd(struct gdrom *gd, int arg) {
|
||||
enum gd_ata_cmd cmd = (enum gd_ata_cmd)arg;
|
||||
int read_data = 0;
|
||||
|
||||
LOG_GDROM("gdrom_ata_cmd 0x%x", cmd);
|
||||
|
||||
gd->error.ABRT = 0;
|
||||
gd->status.DRDY = 0;
|
||||
gd->status.BSY = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case ATA_NOP: {
|
||||
gd->error.ABRT = 1;
|
||||
} break;
|
||||
|
||||
case ATA_SOFT_RESET: {
|
||||
gdrom_set_disc(gd, gd->disc);
|
||||
} break;
|
||||
|
||||
/*case ATA_EXEC_DIAG: {
|
||||
LOG_FATAL("Unhandled");
|
||||
} break;*/
|
||||
|
||||
case ATA_PACKET_CMD: {
|
||||
read_data = 1;
|
||||
} break;
|
||||
|
||||
/*case ATA_IDENTIFY_DEV: {
|
||||
LOG_FATAL("Unhandled");
|
||||
} break;*/
|
||||
|
||||
case ATA_SET_FEATURES: {
|
||||
/* transfer mode settings are ignored */
|
||||
} break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("Unsupported ATA command %d", cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_data) {
|
||||
gd->pio_head = 0;
|
||||
|
||||
gd->ireason.CoD = 1;
|
||||
gd->ireason.IO = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
gd->state = STATE_READ_ATA_DATA;
|
||||
} else {
|
||||
gdrom_ata_end(gd);
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_event(struct gdrom *gd, enum gd_event ev, int arg) {
|
||||
gd_event_cb cb = gd_transitions[gd->state][ev];
|
||||
LOG_INFO("state %d, ev %d, cb %p", gd->state, ev, cb);
|
||||
CHECK(cb);
|
||||
cb(gd, arg);
|
||||
}
|
||||
|
||||
void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data) {
|
||||
|
@ -109,7 +426,7 @@ void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data) {
|
|||
memset(data, 0, GD_MAX_SUBCODE_SIZE);
|
||||
data[1] = AST_NOSTATUS;
|
||||
|
||||
LOG_INFO("GetSubcode not fully implemented");
|
||||
LOG_INFO("gdrom_get_subcode not fully implemented");
|
||||
}
|
||||
|
||||
void gdrom_get_session(struct gdrom *gd, int session, struct gd_session *ses) {
|
||||
|
@ -175,171 +492,6 @@ void gdrom_get_toc(struct gdrom *gd, enum gd_area area_type,
|
|||
toc->leadout.fad = SWAP_24(leadout_fad);
|
||||
}
|
||||
|
||||
static void gdrom_ata_cmd(struct gdrom *gd, enum gd_ata_cmd cmd) {
|
||||
LOG_GDROM("gdrom_ata_cmd 0x%x", cmd);
|
||||
|
||||
gd->status.DRDY = 0;
|
||||
gd->status.BSY = 1;
|
||||
|
||||
switch (cmd) {
|
||||
case ATA_NOP:
|
||||
/* Setting "abort" in the error register */
|
||||
/* Setting "error" in the status register */
|
||||
/* Clearing BUSY in the status register */
|
||||
/* Asserting the INTRQ signal */
|
||||
gdrom_event(gd, EV_ATA_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
case ATA_SOFT_RESET:
|
||||
gdrom_set_disc(gd, gd->disc);
|
||||
gdrom_event(gd, EV_ATA_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
/*case ATA_EXEC_DIAG:
|
||||
LOG_FATAL("Unhandled");
|
||||
break;*/
|
||||
|
||||
case ATA_PACKET:
|
||||
gdrom_event(gd, EV_SPI_WAIT_CMD, 0, 0);
|
||||
break;
|
||||
|
||||
/*case ATA_IDENTIFY_DEV:
|
||||
LOG_FATAL("Unhandled");
|
||||
break;*/
|
||||
|
||||
case ATA_SET_FEATURES:
|
||||
/* FIXME I think we're supposed to be honoring GD_SECTCNT here to control
|
||||
the DMA setting used by CD_READ */
|
||||
gdrom_event(gd, EV_ATA_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("Unsupported ATA command %d", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void gdrom_spi_cmd(struct gdrom *gd, uint8_t *data) {
|
||||
enum gd_spi_cmd cmd = (enum gd_spi_cmd)data[0];
|
||||
|
||||
LOG_GDROM("gdrom_spi_cmd 0x%x", cmd);
|
||||
|
||||
gd->status.DRQ = 0;
|
||||
gd->status.BSY = 1;
|
||||
|
||||
switch (cmd) {
|
||||
/*
|
||||
* Packet Command Flow For PIO DATA To Host
|
||||
*/
|
||||
case SPI_REQ_STAT: {
|
||||
int addr = data[2];
|
||||
int sz = data[4];
|
||||
uint8_t stat[10];
|
||||
stat[0] = gd->sectnum.status;
|
||||
stat[1] = gd->sectnum.format << 4;
|
||||
stat[2] = 0x4;
|
||||
stat[3] = 2;
|
||||
stat[4] = 0;
|
||||
stat[5] = 0;
|
||||
stat[6] = 0;
|
||||
stat[7] = 0;
|
||||
stat[8] = 0;
|
||||
stat[9] = 0;
|
||||
gdrom_event(gd, EV_SPI_WRITE_START, (intptr_t)&stat[addr], sz);
|
||||
} break;
|
||||
|
||||
case SPI_REQ_MODE: {
|
||||
int offset = data[2];
|
||||
int sz = data[4];
|
||||
gdrom_event(gd, EV_SPI_WRITE_START,
|
||||
(intptr_t)((void *)&gd->hw_info + offset), sz);
|
||||
} break;
|
||||
|
||||
/*case SPI_REQ_ERROR:
|
||||
break;*/
|
||||
|
||||
case SPI_GET_TOC: {
|
||||
enum gd_area area_type = (enum gd_area)(data[1] & 0x1);
|
||||
int size = (data[3] << 8) | data[4];
|
||||
struct gd_toc toc;
|
||||
gdrom_get_toc(gd, area_type, &toc);
|
||||
gdrom_event(gd, EV_SPI_WRITE_START, (intptr_t)&toc, size);
|
||||
} break;
|
||||
|
||||
case SPI_REQ_SES: {
|
||||
int session = data[2];
|
||||
int size = data[4];
|
||||
struct gd_session ses;
|
||||
gdrom_get_session(gd, session, &ses);
|
||||
gdrom_event(gd, EV_SPI_WRITE_START, (intptr_t)&ses, size);
|
||||
} break;
|
||||
|
||||
case SPI_GET_SCD: {
|
||||
int format = data[1] & 0xf;
|
||||
int size = (data[3] << 8) | data[4];
|
||||
uint8_t scd[GD_MAX_SUBCODE_SIZE];
|
||||
gdrom_get_subcode(gd, format, scd);
|
||||
gdrom_event(gd, EV_SPI_WRITE_START, (intptr_t)scd, size);
|
||||
} break;
|
||||
|
||||
case SPI_CD_READ: {
|
||||
int msf = (data[1] & 0x1);
|
||||
|
||||
gd->req.dma = gd->features.dma;
|
||||
gd->req.sector_fmt = (enum gd_secfmt)((data[1] & 0xe) >> 1);
|
||||
gd->req.sector_mask = (enum gd_secmask)((data[1] >> 4) & 0xff);
|
||||
gd->req.first_sector = gdrom_get_fad(data[2], data[3], data[4], msf);
|
||||
gd->req.num_sectors = (data[8] << 16) | (data[9] << 8) | data[10];
|
||||
|
||||
CHECK_EQ(gd->req.sector_fmt, SECTOR_M1);
|
||||
|
||||
gdrom_event(gd, EV_SPI_WRITE_SECTORS, 0, 0);
|
||||
} break;
|
||||
|
||||
/*case SPI_CD_READ2:
|
||||
break;*/
|
||||
|
||||
/*
|
||||
* Transfer Packet Command Flow For PIO Data from Host
|
||||
*/
|
||||
case SPI_SET_MODE: {
|
||||
int offset = data[2];
|
||||
int size = data[4];
|
||||
gdrom_event(gd, EV_SPI_READ_START, offset, size);
|
||||
} break;
|
||||
|
||||
/*
|
||||
* Non-Data Command Flow
|
||||
*/
|
||||
case SPI_TEST_UNIT:
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
case SPI_CD_OPEN:
|
||||
case SPI_CD_PLAY:
|
||||
case SPI_CD_SEEK:
|
||||
case SPI_CD_SCAN:
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
/* 0x70 and 0x71 appear to be a part of some kind of security check that has
|
||||
yet to be properly reverse engineered. be a lemming and reply / set state
|
||||
as expected by various games */
|
||||
case SPI_UNKNOWN_70:
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
break;
|
||||
|
||||
case SPI_UNKNOWN_71:
|
||||
gd->sectnum.status = DST_PAUSE;
|
||||
gdrom_event(gd, EV_SPI_WRITE_START, (intptr_t)reply_71, sizeof(reply_71));
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_FATAL("Unsupported SPI command %d", cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int gdrom_read_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
||||
enum gd_secmask mask, int num_sectors, uint8_t *dst,
|
||||
int dst_size) {
|
||||
|
@ -368,168 +520,6 @@ int gdrom_read_sectors(struct gdrom *gd, int fad, enum gd_secfmt fmt,
|
|||
return total;
|
||||
}
|
||||
|
||||
static void gdrom_event(struct gdrom *gd, enum gd_event ev, intptr_t arg0,
|
||||
intptr_t arg1) {
|
||||
enum gd_state old_state = gd->state;
|
||||
|
||||
switch (ev) {
|
||||
case EV_ATA_CMD_DONE: {
|
||||
CHECK_EQ(gd->state, STATE_STANDBY);
|
||||
|
||||
gd->status.DRDY = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_STANDBY;
|
||||
} break;
|
||||
|
||||
case EV_SPI_WAIT_CMD: {
|
||||
CHECK_EQ(gd->state, STATE_STANDBY);
|
||||
|
||||
gd->pio_head = 0;
|
||||
|
||||
gd->ireason.CoD = 1;
|
||||
gd->ireason.IO = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
gd->state = STATE_SPI_READ_CMD;
|
||||
} break;
|
||||
|
||||
case EV_SPI_READ_START: {
|
||||
CHECK_EQ(gd->state, STATE_SPI_READ_CMD);
|
||||
|
||||
int offset = (int)arg0;
|
||||
int size = (int)arg1;
|
||||
CHECK_NE(size, 0);
|
||||
|
||||
gd->pio_head = 0;
|
||||
gd->pio_size = size;
|
||||
gd->pio_read = offset;
|
||||
|
||||
gd->byte_count.full = size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_SPI_READ_DATA;
|
||||
} break;
|
||||
|
||||
case EV_SPI_READ_END: {
|
||||
CHECK(gd->state == STATE_SPI_READ_CMD ||
|
||||
gd->state == STATE_SPI_READ_DATA);
|
||||
|
||||
if (gd->state == STATE_SPI_READ_CMD) {
|
||||
CHECK_EQ(gd->pio_head, SPI_CMD_SIZE);
|
||||
gdrom_spi_cmd(gd, gd->pio_buffer);
|
||||
} else if (gd->state == STATE_SPI_READ_DATA) {
|
||||
gdrom_set_mode(gd, gd->pio_read, gd->pio_buffer, gd->pio_head);
|
||||
}
|
||||
} break;
|
||||
|
||||
case EV_SPI_WRITE_START: {
|
||||
CHECK_EQ(gd->state, STATE_SPI_READ_CMD);
|
||||
|
||||
uint8_t *data = (uint8_t *)arg0;
|
||||
int size = (int)arg1;
|
||||
|
||||
CHECK(size && size < (int)sizeof(gd->pio_buffer));
|
||||
memcpy(gd->pio_buffer, data, size);
|
||||
gd->pio_size = size;
|
||||
gd->pio_head = 0;
|
||||
|
||||
gd->byte_count.full = gd->pio_size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_SPI_WRITE_DATA;
|
||||
} break;
|
||||
|
||||
case EV_SPI_WRITE_SECTORS: {
|
||||
CHECK(gd->state == STATE_SPI_READ_CMD ||
|
||||
gd->state == STATE_SPI_WRITE_SECTORS);
|
||||
|
||||
if (gd->req.dma) {
|
||||
int max_dma_size = gd->req.num_sectors * SECTOR_SIZE;
|
||||
CHECK_LT(max_dma_size, (int)sizeof(gd->dma_buffer));
|
||||
|
||||
/* read to DMA buffer */
|
||||
gd->dma_size = gdrom_read_sectors(
|
||||
gd, gd->req.first_sector, gd->req.sector_fmt, gd->req.sector_mask,
|
||||
gd->req.num_sectors, gd->dma_buffer, sizeof(gd->dma_buffer));
|
||||
gd->dma_head = 0;
|
||||
|
||||
/* gdrom state won't be updated until DMA transfer is completed */
|
||||
} else {
|
||||
int max_pio_sectors = sizeof(gd->pio_buffer) / SECTOR_SIZE;
|
||||
|
||||
/* fill PIO buffer with as many sectors as possible */
|
||||
int num_sectors = MIN(gd->req.num_sectors, max_pio_sectors);
|
||||
gd->pio_size = gdrom_read_sectors(
|
||||
gd, gd->req.first_sector, gd->req.sector_fmt, gd->req.sector_mask,
|
||||
num_sectors, gd->pio_buffer, (int)sizeof(gd->pio_buffer));
|
||||
gd->pio_head = 0;
|
||||
|
||||
/* update sector read state */
|
||||
gd->req.first_sector += num_sectors;
|
||||
gd->req.num_sectors -= num_sectors;
|
||||
|
||||
/* update gdrom state */
|
||||
gd->byte_count.full = gd->pio_size;
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 0;
|
||||
gd->status.DRQ = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
}
|
||||
|
||||
gd->state = STATE_SPI_WRITE_SECTORS;
|
||||
} break;
|
||||
|
||||
case EV_SPI_WRITE_END: {
|
||||
CHECK(gd->state == STATE_SPI_WRITE_DATA ||
|
||||
gd->state == STATE_SPI_WRITE_SECTORS);
|
||||
|
||||
/* if there are still sectors remaining to be written out to the PIO
|
||||
buffer, continue doing so */
|
||||
if (gd->state == STATE_SPI_WRITE_SECTORS && gd->req.num_sectors) {
|
||||
gdrom_event(gd, EV_SPI_WRITE_SECTORS, 0, 0);
|
||||
} else {
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
}
|
||||
} break;
|
||||
|
||||
case EV_SPI_CMD_DONE: {
|
||||
CHECK(gd->state == STATE_SPI_READ_CMD ||
|
||||
gd->state == STATE_SPI_READ_DATA ||
|
||||
gd->state == STATE_SPI_WRITE_DATA ||
|
||||
gd->state == STATE_SPI_WRITE_SECTORS);
|
||||
|
||||
gd->ireason.IO = 1;
|
||||
gd->ireason.CoD = 1;
|
||||
gd->status.DRDY = 1;
|
||||
gd->status.BSY = 0;
|
||||
gd->status.DRQ = 0;
|
||||
|
||||
holly_raise_interrupt(gd->holly, HOLLY_INTC_G1GDINT);
|
||||
|
||||
gd->state = STATE_STANDBY;
|
||||
} break;
|
||||
}
|
||||
|
||||
LOG_GDROM("gdrom_event %d, old_state %d, new_state %d", ev, old_state,
|
||||
gd->state);
|
||||
}
|
||||
|
||||
static int gdrom_init(struct device *dev) {
|
||||
struct gdrom *gd = (struct gdrom *)dev;
|
||||
|
||||
|
@ -555,21 +545,27 @@ void gdrom_dma_end(struct gdrom *gd) {
|
|||
LOG_GDROM("gd_dma_end, %d / %d read from dma buffer", gd->dma_head,
|
||||
gd->dma_size);
|
||||
|
||||
if (gd->dma_head < gd->dma_size) {
|
||||
if (gd->dma_head >= gd->dma_size) {
|
||||
/* CD_READ command is now done */
|
||||
gdrom_spi_end(gd);
|
||||
return;
|
||||
}
|
||||
|
||||
/* CD_READ command is now done */
|
||||
gdrom_event(gd, EV_SPI_CMD_DONE, 0, 0);
|
||||
}
|
||||
|
||||
int gdrom_dma_read(struct gdrom *gd, uint8_t *data, int n) {
|
||||
/* try to read more if the current dma buffer has been completely read */
|
||||
if (gd->dma_head >= gd->dma_size) {
|
||||
gdrom_spi_cdread(gd);
|
||||
}
|
||||
|
||||
int remaining = gd->dma_size - gd->dma_head;
|
||||
n = MIN(n, remaining);
|
||||
CHECK_GT(n, 0);
|
||||
|
||||
LOG_GDROM("gdrom_dma_read %d / %d bytes", gd->dma_head + n, gd->dma_size);
|
||||
memcpy(data, &gd->dma_buffer[gd->dma_head], n);
|
||||
gd->dma_head += n;
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
|
@ -588,14 +584,18 @@ void gdrom_set_disc(struct gdrom *gd, struct disc *disc) {
|
|||
gd->disc = disc;
|
||||
}
|
||||
|
||||
/* looking at "6.1.1 CD Drive State Transition Diagram" in CDIF131E.pdf, it
|
||||
seems standby is the default state for when a disc is inserted */
|
||||
gd->sectnum.status = gd->disc ? DST_STANDBY : DST_NODISC;
|
||||
gd->sectnum.format = DISC_GDROM;
|
||||
/* perform "soft reset" of internal state */
|
||||
gd->error.full = 0;
|
||||
|
||||
gd->status.full = 0;
|
||||
gd->status.DRDY = 1;
|
||||
gd->status.BSY = 0;
|
||||
|
||||
gd->sectnum.full = 0;
|
||||
gd->sectnum.status = gd->disc ? DST_STANDBY : DST_NODISC;
|
||||
gd->sectnum.format = DISC_GDROM;
|
||||
|
||||
/* TODO how do GD_FEATURES, GD_INTREASON, GD_BYCTLLO and GD_BYCTLHI behave */
|
||||
}
|
||||
|
||||
int gdrom_disk_format(struct gdrom *gd) {
|
||||
|
@ -648,9 +648,8 @@ REG_R32(holly_cb, GD_DATA) {
|
|||
LOG_GDROM("read GD_DATA 0x%x", value);
|
||||
|
||||
gd->pio_head += 2;
|
||||
if (gd->pio_head == gd->pio_size) {
|
||||
gdrom_event(gd, EV_SPI_WRITE_END, 0, 0);
|
||||
}
|
||||
|
||||
gdrom_event(gd, EVENT_PIO_READ, 0);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
@ -663,16 +662,13 @@ REG_W32(holly_cb, GD_DATA) {
|
|||
*(uint16_t *)&gd->pio_buffer[gd->pio_head] = (uint16_t)(value & 0xffff);
|
||||
gd->pio_head += 2;
|
||||
|
||||
/* check if we've finished reading a command / the remaining data */
|
||||
if ((gd->state == STATE_SPI_READ_CMD && gd->pio_head == SPI_CMD_SIZE) ||
|
||||
(gd->state == STATE_SPI_READ_DATA && gd->pio_head == gd->pio_size)) {
|
||||
gdrom_event(gd, EV_SPI_READ_END, 0, 0);
|
||||
}
|
||||
gdrom_event(gd, EVENT_PIO_WRITE, 0);
|
||||
}
|
||||
|
||||
REG_R32(holly_cb, GD_ERROR_FEATURES) {
|
||||
uint16_t value = 0;
|
||||
LOG_GDROM("read GD_ERROR 0x%x [unimplemented]", value);
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
uint16_t value = gd->error.full;
|
||||
LOG_FATAL("read GD_ERROR 0x%x", value);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -751,5 +747,5 @@ REG_R32(holly_cb, GD_STATUS_COMMAND) {
|
|||
REG_W32(holly_cb, GD_STATUS_COMMAND) {
|
||||
struct gdrom *gd = dc->gdrom;
|
||||
LOG_GDROM("write GD_STATUS_COMMAND 0x%x", value);
|
||||
gdrom_ata_cmd(gd, (enum gd_ata_cmd)value);
|
||||
gdrom_event(gd, EVENT_ATA_CMD, value);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ enum gd_ata_cmd {
|
|||
ATA_NOP = 0x00,
|
||||
ATA_SOFT_RESET = 0x08,
|
||||
ATA_EXEC_DIAG = 0x90,
|
||||
ATA_PACKET = 0xa0,
|
||||
ATA_PACKET_CMD = 0xa0,
|
||||
ATA_IDENTIFY_DEV = 0xa1,
|
||||
ATA_SET_FEATURES = 0xef,
|
||||
};
|
||||
|
@ -107,11 +107,23 @@ struct gd_session {
|
|||
uint32_t start_fad : 24;
|
||||
};
|
||||
|
||||
union gd_error {
|
||||
uint32_t full;
|
||||
struct {
|
||||
uint32_t ILI : 1;
|
||||
uint32_t EOMF : 1;
|
||||
uint32_t ABRT : 1;
|
||||
uint32_t MCR : 1;
|
||||
uint32_t sense_key : 4;
|
||||
uint32_t : 24;
|
||||
};
|
||||
};
|
||||
|
||||
union gd_features {
|
||||
uint32_t full;
|
||||
struct {
|
||||
uint32_t dma : 1;
|
||||
uint32_t reserved : 31;
|
||||
uint32_t : 31;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue