refactored gdrom state machine to have more formalized transitions

This commit is contained in:
Anthony Pesch 2017-05-30 17:07:27 -04:00
parent 525dabd4c5
commit e3e7975a6c
2 changed files with 395 additions and 387 deletions

View File

@ -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);
}

View File

@ -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;
};
};