nkeynes@31: /** nkeynes@258: * $Id: ide.c,v 1.22 2006-12-29 00:23:13 nkeynes Exp $ nkeynes@2: * nkeynes@31: * IDE interface implementation nkeynes@31: * nkeynes@152: * nkeynes@152: * Note: All references to read/write are from the host's point of view. nkeynes@152: * nkeynes@152: * See: INF-8020 nkeynes@31: * Copyright (c) 2005 Nathan Keynes. nkeynes@31: * nkeynes@31: * This program is free software; you can redistribute it and/or modify nkeynes@31: * it under the terms of the GNU General Public License as published by nkeynes@31: * the Free Software Foundation; either version 2 of the License, or nkeynes@31: * (at your option) any later version. nkeynes@31: * nkeynes@31: * This program is distributed in the hope that it will be useful, nkeynes@31: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@31: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@31: * GNU General Public License for more details. nkeynes@2: */ nkeynes@35: nkeynes@35: #define MODULE ide_module nkeynes@35: nkeynes@138: #include nkeynes@2: #include nkeynes@35: #include "dream.h" nkeynes@125: #include "asic.h" nkeynes@39: #include "gdrom/ide.h" nkeynes@125: #include "gdrom/gdrom.h" nkeynes@142: #include "gdrom/packet.h" nkeynes@2: nkeynes@138: #define MAX_WRITE_BUF 4096 nkeynes@138: #define MAX_SECTOR_SIZE 2352 /* Audio sector */ nkeynes@138: #define DEFAULT_DATA_SECTORS 8 nkeynes@2: nkeynes@138: static void ide_init( void ); nkeynes@138: static void ide_reset( void ); nkeynes@138: static void ide_save_state( FILE *f ); nkeynes@138: static int ide_load_state( FILE *f ); nkeynes@138: static void ide_raise_interrupt( void ); nkeynes@138: static void ide_clear_interrupt( void ); nkeynes@152: static void ide_packet_command( unsigned char *data ); nkeynes@15: nkeynes@15: struct dreamcast_module ide_module = { "IDE", ide_init, ide_reset, NULL, NULL, nkeynes@138: NULL, ide_save_state, ide_load_state }; nkeynes@15: nkeynes@2: struct ide_registers idereg; nkeynes@138: unsigned char *data_buffer = NULL; nkeynes@138: uint32_t data_buffer_len = 0; nkeynes@2: nkeynes@152: #define WRITE_BUFFER(x16) *((uint16_t *)(data_buffer + idereg.data_offset)) = x16 nkeynes@152: #define READ_BUFFER() *((uint16_t *)(data_buffer + idereg.data_offset)) nkeynes@152: nkeynes@2: /* "\0\0\0\0\xb4\x19\0\0\x08SE REV 6.42990316" */ nkeynes@125: unsigned char gdrom_ident[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xb4, 0x19, 0x00, nkeynes@2: 0x00, 0x08, 0x53, 0x45, 0x20, 0x20, 0x20, 0x20, nkeynes@2: 0x20, 0x20, 0x52, 0x65, 0x76, 0x20, 0x36, 0x2e, nkeynes@2: 0x34, 0x32, 0x39, 0x39, 0x30, 0x33, 0x31, 0x36 }; nkeynes@2: nkeynes@158: unsigned char gdrom_71[] = { 0x3E, 0x0F, 0x90, 0xBE, 0x1D, 0xD9, 0x89, 0x04, 0x28, 0x3A, 0x8E, 0x26, 0x5C, 0x95, 0x10, 0x5A, nkeynes@158: 0x0A, 0x99, 0xEE, 0xFB, 0x69, 0xCE, 0xD9, 0x63, 0x00, 0xF5, 0x0A, 0xBC, 0x2C, 0x0D, 0xF8, 0xE2, nkeynes@158: 0x05, 0x02, 0x00, 0x7C, 0x03, 0x00, 0x3D, 0x08, 0xD8, 0x8D, 0x08, 0x7A, 0x6D, 0x00, 0x35, 0x06, nkeynes@158: 0xBA, 0x66, 0x10, 0x00, 0x91, 0x08, 0x10, 0x29, 0xD0, 0x45, 0xDA, 0x00, 0x2D, 0x05, 0x69, 0x09, nkeynes@158: 0x00, 0x5E, 0x0F, 0x70, 0x86, 0x12, 0x6C, 0x77, 0x5A, 0xFB, 0xCD, 0x56, 0xFB, 0xF7, 0xB7, 0x00, nkeynes@158: 0x5D, 0x07, 0x19, 0x99, 0xF2, 0xAF, 0x00, 0x63, 0x03, 0x00, 0xF0, 0x10, 0xBE, 0xD7, 0xA0, 0x63, nkeynes@158: 0xFA, 0x84, 0xA7, 0x74, 0x94, 0xEF, 0xAD, 0xC2, 0xAC, 0x00, 0x78, 0x07, 0x9F, 0x57, 0x0B, 0x62, nkeynes@158: 0x00, 0xFE, 0x08, 0x08, 0x5D, 0x5A, 0x6A, 0x54, 0x00, 0xE2, 0x09, 0x93, 0x7E, 0x62, 0x2A, 0x5E, nkeynes@158: 0xDA, 0x00, 0x7E, 0x0F, 0xF0, 0x07, 0x01, 0x6D, 0x50, 0x86, 0xDD, 0x4A, 0x15, 0x54, 0xC7, 0xEC, nkeynes@158: 0x00, 0xF2, 0x0B, 0x07, 0xF8, 0x1A, 0xB0, 0x99, 0x3B, 0xF1, 0x36, 0x00, 0x94, 0x07, 0x34, 0xE3, nkeynes@158: 0xBC, 0x6E, 0x00, 0x34, 0x0D, 0x6F, 0xDA, 0xBD, 0xEE, 0xF7, 0xCC, 0xCE, 0x39, 0x7E, 0xE3, 0x00, nkeynes@158: 0x14, 0x08, 0xDC, 0xD2, 0xB9, 0xF9, 0x31, 0x00, 0xB0, 0x0C, 0x10, 0xA3, 0x45, 0x12, 0xC7, 0xCD, nkeynes@158: 0xBF, 0x05, 0x37, 0x00, 0xC4, 0x0D, 0x5F, 0xE0, 0x59, 0xBB, 0x01, 0x59, 0x03, 0xD6, 0x29, 0x9C, nkeynes@158: 0x00, 0x01, 0x0A, 0x09, 0xAA, 0xA8, 0xA8, 0x24, 0x0B, 0x66, 0x00, 0x5C, 0x05, 0xA5, 0xCE, 0x00, nkeynes@158: 0xC1, 0x0B, 0xB7, 0xA0, 0x6F, 0xE9, 0x2B, 0xCC, 0xB5, 0xFC, 0x00, 0x8D, 0x05, 0xF4, 0xAC, 0x00, nkeynes@158: 0x57, 0x04, 0xB6, 0x00, 0xFC, 0x03, 0x00, 0xC3, 0x10, 0x43, 0x3B, 0xBE, 0xA2, 0x96, 0xC3, 0x65, nkeynes@158: 0x9F, 0x9A, 0x88, 0xD5, 0x49, 0x68, 0x00, 0xDC, 0x11, 0x56, 0x23, 0x2D, 0xF9, 0xFC, 0xF5, 0x8B, nkeynes@158: 0x1B, 0xB1, 0xB7, 0x10, 0x21, 0x1C, 0x12, 0x00, 0x0D, 0x0D, 0xEB, 0x86, 0xA2, 0x49, 0x8D, 0x8D, nkeynes@158: 0xBE, 0xA1, 0x6D, 0x53, 0x00, 0xE1, 0x0A, 0x8E, 0x67, 0xAA, 0x16, 0x79, 0x39, 0x59, 0x00, 0x36, nkeynes@158: 0x0B, 0x2A, 0x4E, 0xAE, 0x51, 0x4B, 0xD0, 0x66, 0x33, 0x00, 0x8A, 0x07, 0xCD, 0x6F, 0xBA, 0x92, nkeynes@158: 0x00, 0x1A, 0x0E, 0xDF, 0x4A, 0xB3, 0x77, 0x1F, 0xA5, 0x90, 0x19, 0xFA, 0x59, 0xD7, 0x00, 0x04, nkeynes@158: 0x0F, 0xAC, 0xCA, 0x9F, 0xA4, 0xFC, 0x6D, 0x90, 0x86, 0x9E, 0x1F, 0x44, 0x40, 0x00, 0x9F, 0x04, nkeynes@158: 0x56, 0x00, 0x22, 0x03, 0x00, 0xB8, 0x10, 0x2C, 0x7A, 0x53, 0xA4, 0xBF, 0xA3, 0x90, 0x90, 0x14, nkeynes@158: 0x9D, 0x46, 0x6C, 0x96, 0x00, 0xC6, 0x0B, 0x9B, 0xBB, 0xB0, 0xAE, 0x60, 0x92, 0x8E, 0x0C, 0x00, nkeynes@158: 0x14, 0x06, 0x4B, 0xAE, 0x7F, 0x00, 0x5C, 0x0B, 0x23, 0xFA, 0xE7, 0x51, 0xDA, 0x61, 0x49, 0x5E, nkeynes@158: 0x00, 0xD7, 0x0B, 0x01, 0xFC, 0x55, 0x31, 0x84, 0xC5, 0x0C, 0x98, 0x00, 0x97, 0x50, 0x6E, 0xF9, nkeynes@158: 0xEE, 0x75, 0x92, 0x53, 0xD3, 0x66, 0xA4, 0xAF, 0x3B, 0xFE, 0x7B, 0x27, 0x30, 0xBB, 0xB6, 0xF2, nkeynes@158: 0x76, 0x22, 0x45, 0x42, 0xCA, 0xF9, 0xF0, 0xDE, 0x9F, 0x45, 0x16, 0x68, 0x22, 0xB9, 0x84, 0x28, nkeynes@158: 0x8F, 0x2B, 0xB5, 0x5C, 0xD2, 0xF5, 0x45, 0x36, 0x3E, 0x76, 0xC6, 0xBF, 0x32, 0x5C, 0x41, 0xA6, nkeynes@158: 0x26, 0xC7, 0x82, 0x2F, 0x2E, 0xB5, 0x75, 0xC6, 0xE6, 0x67, 0x9E, 0x77, 0x94, 0xAF, 0x6A, 0x05, nkeynes@158: 0xC0, 0x05, 0x61, 0x71, 0x89, 0x5A, 0xB1, 0xD0, 0xFC, 0x7E, 0xC0, 0x9B, 0xCB, 0x3B, 0x69, 0xD9, nkeynes@158: 0x5F, 0xAF, 0xCA, 0xAB, 0x25, 0xD5, 0xBE, 0x8A, 0x6B, 0xB0, 0xFB, 0x61, 0x6C, 0xEB, 0x85, 0x6E, nkeynes@158: 0x7A, 0x48, 0xFF, 0x97, 0x91, 0x06, 0x3D, 0x4D, 0x68, 0xD3, 0x65, 0x83, 0x90, 0xA0, 0x08, 0x5C, nkeynes@158: 0xFC, 0xEE, 0x7C, 0x33, 0x43, 0x7F, 0x80, 0x52, 0x8B, 0x19, 0x72, 0xF2, 0xC9, 0xAB, 0x93, 0xAF, nkeynes@158: 0x16, 0xED, 0x36, 0x48, 0xAB, 0xC9, 0xD1, 0x03, 0xB3, 0xDC, 0x2F, 0xF2, 0x92, 0x3F, 0x0A, 0x19, nkeynes@158: 0x25, 0xE2, 0xEF, 0x7A, 0x22, 0xDA, 0xDB, 0xCB, 0x32, 0x12, 0x61, 0x49, 0x5B, 0x74, 0x7C, 0x65, nkeynes@158: 0x20, 0x89, 0x54, 0x9E, 0x0E, 0xC9, 0x52, 0xE3, 0xC9, 0x9A, 0x44, 0xC9, 0x5D, 0xA6, 0x77, 0xC0, nkeynes@158: 0xE7, 0x60, 0x91, 0x80, 0x50, 0x1F, 0x33, 0xB1, 0xCD, 0xAD, 0xF4, 0x0D, 0xBB, 0x08, 0xB1, 0xD0, nkeynes@158: 0x13, 0x95, 0xAE, 0xC9, 0xE2, 0x64, 0xA2, 0x65, 0xFB, 0x8F, 0xE9, 0xA2, 0x8A, 0xBC, 0x98, 0x81, nkeynes@158: 0x45, 0xB4, 0x55, 0x4E, 0xB9, 0x74, 0xB4, 0x50, 0x76, 0xBF, 0xF0, 0x45, 0xE7, 0xEE, 0x41, 0x64, nkeynes@158: 0x9F, 0xB5, 0xE0, 0xBB, 0x1C, 0xBB, 0x28, 0x66, 0x1B, 0xDD, 0x2B, 0x02, 0x66, 0xBF, 0xFD, 0x7D, nkeynes@158: 0x37, 0x35, 0x1D, 0x76, 0x21, 0xC3, 0x8F, 0xAF, 0xF6, 0xF9, 0xE9, 0x27, 0x48, 0xE7, 0x3D, 0x95, nkeynes@158: 0x74, 0x0C, 0x77, 0x88, 0x56, 0xD9, 0x84, 0xC8, 0x7D, 0x20, 0x31, 0x43, 0x53, 0xF1, 0xC1, 0xC7, nkeynes@158: 0xC9, 0xF7, 0x5C, 0xC0, 0xA6, 0x5A, 0x27, 0x0A, 0x41, 0xD4, 0x44, 0x94, 0x65, 0x4F, 0xE2, 0x53, nkeynes@158: 0x60, 0x0B, 0xD1, 0x23, 0x6C, 0x0C, 0xBC, 0x70, 0x6C, 0x26, 0x1A, 0x61, 0x1D, 0x35, 0x88, 0xEC, nkeynes@158: 0xB8, 0x15, 0xE3, 0xB4, 0x82, 0xEE, 0xB3, 0x21, 0xAC, 0x6C, 0xB7, 0x33, 0x6D, 0x78, 0x0C, 0x0D, nkeynes@158: 0xB4, 0x0B, 0x29, 0xF2, 0xD4, 0x8C, 0x3F, 0xDD, 0x3F, 0x47, 0xDD, 0xF2, 0xD8, 0x39, 0x57, 0x20, nkeynes@158: 0x28, 0xD8, 0xDD, 0x32, 0xE2, 0x6A, 0x47, 0x53, 0x57, 0xC6, 0xFA, 0x7A, 0x38, 0x30, 0x31, 0x8F, nkeynes@158: 0xE7, 0xD3, 0x84, 0x2B, 0x5D, 0x4F, 0x95, 0x98, 0xED, 0x0B, 0xD7, 0x50, 0x0C, 0x49, 0xDA, 0x59, nkeynes@158: 0x15, 0xF1, 0x39, 0xF3, 0x40, 0xDC, 0xDC, 0x25, 0x24, 0x56, 0x6E, 0xA9, 0x2F, 0xF0, 0x00, 0x00 }; nkeynes@158: nkeynes@2: nkeynes@250: char gdrom_status[] = { nkeynes@250: 0x00, 0x15, 0x00, 0x64, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, nkeynes@250: 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, nkeynes@250: 0x40, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, nkeynes@250: 0x00, 0x40, 0x40, 0x00, 0x00, 0x00, 0x40, 0x40, nkeynes@250: 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40, 0x00, nkeynes@250: 0x00, 0x40, 0x00, 0x00 }; nkeynes@250: nkeynes@250: nkeynes@138: static void ide_init( void ) nkeynes@138: { nkeynes@138: ide_reset(); nkeynes@138: data_buffer_len = DEFAULT_DATA_SECTORS; nkeynes@138: data_buffer = malloc( MAX_SECTOR_SIZE * data_buffer_len ); nkeynes@138: assert( data_buffer != NULL ); nkeynes@138: } nkeynes@138: nkeynes@138: static void ide_reset( void ) nkeynes@138: { nkeynes@138: ide_clear_interrupt(); nkeynes@138: idereg.error = 0x01; nkeynes@138: idereg.count = 0x01; nkeynes@138: idereg.lba0 = /* 0x21; */ 0x81; nkeynes@138: idereg.lba1 = 0x14; nkeynes@138: idereg.lba2 = 0xeb; nkeynes@138: idereg.feature = 0; /* Indeterminate really */ nkeynes@254: idereg.status = 0x00; nkeynes@138: idereg.device = 0x00; nkeynes@143: idereg.disc = gdrom_is_mounted() ? (IDE_DISC_CDROM|IDE_DISC_READY) : IDE_DISC_NONE; nkeynes@152: idereg.state = IDE_STATE_IDLE; nkeynes@152: memset( idereg.gdrom_sense, '\0', 10 ); nkeynes@152: idereg.data_offset = -1; nkeynes@152: idereg.data_length = -1; nkeynes@245: idereg.last_read_track = 1; nkeynes@245: idereg.last_read_lba = 150; nkeynes@254: idereg.was_reset = TRUE; nkeynes@138: } nkeynes@138: nkeynes@138: static void ide_save_state( FILE *f ) nkeynes@138: { nkeynes@152: fwrite( &idereg, sizeof(idereg), 1, f ); nkeynes@152: fwrite( &data_buffer_len, sizeof(data_buffer_len), 1, f ); nkeynes@158: fwrite( data_buffer, MAX_SECTOR_SIZE, data_buffer_len, f ); nkeynes@138: } nkeynes@138: nkeynes@138: static int ide_load_state( FILE *f ) nkeynes@138: { nkeynes@152: uint32_t length; nkeynes@152: fread( &idereg, sizeof(idereg), 1, f ); nkeynes@152: fread( &length, sizeof(uint32_t), 1, f ); nkeynes@152: if( length > data_buffer_len ) { nkeynes@152: if( data_buffer != NULL ) nkeynes@152: free( data_buffer ); nkeynes@158: data_buffer = malloc( MAX_SECTOR_SIZE * length ); nkeynes@152: assert( data_buffer != NULL ); nkeynes@152: data_buffer_len = length; nkeynes@152: } nkeynes@158: fread( data_buffer, MAX_SECTOR_SIZE, length, f ); nkeynes@138: return 0; nkeynes@138: } nkeynes@125: nkeynes@152: /************************ State transitions *************************/ nkeynes@152: nkeynes@158: void ide_set_packet_result( uint16_t result ) nkeynes@158: { nkeynes@158: idereg.gdrom_sense[0] = 0xf0; nkeynes@158: idereg.gdrom_sense[2] = result & 0xFF; nkeynes@158: idereg.gdrom_sense[8] = (result >> 8) & 0xFF; nkeynes@158: idereg.error = (result & 0x0F) << 4; nkeynes@254: idereg.count = 3; nkeynes@158: if( result != 0 ) { nkeynes@158: idereg.status = 0x51; nkeynes@158: ide_raise_interrupt(); nkeynes@158: } else { nkeynes@158: idereg.status = idereg.status & ~(IDE_STATUS_BSY|IDE_STATUS_CHK); nkeynes@158: } nkeynes@158: } nkeynes@158: nkeynes@152: /** nkeynes@152: * Begin command packet write to the device. This is always 12 bytes of PIO data nkeynes@152: */ nkeynes@152: static void ide_start_command_packet_write( ) nkeynes@2: { nkeynes@152: idereg.state = IDE_STATE_CMD_WRITE; nkeynes@254: idereg.status = 0x58; nkeynes@152: idereg.error = idereg.feature & 0x03; /* Copy values of OVL/DMA */ nkeynes@152: idereg.count = IDE_COUNT_CD; nkeynes@152: idereg.data_offset = 0; nkeynes@152: idereg.data_length = 12; nkeynes@2: } nkeynes@2: nkeynes@152: /** nkeynes@152: * Begin PIO read from the device. The data is assumed to already be nkeynes@152: * in the buffer at this point. nkeynes@152: */ nkeynes@152: static void ide_start_read( int length, int blocksize, gboolean dma ) nkeynes@2: { nkeynes@152: idereg.count = IDE_COUNT_IO; nkeynes@152: idereg.data_length = length; nkeynes@152: idereg.data_offset = 0; nkeynes@152: if( dma ) { nkeynes@152: idereg.state = IDE_STATE_DMA_READ; nkeynes@158: idereg.status = 0xD0; nkeynes@152: } else { nkeynes@152: idereg.state = IDE_STATE_PIO_READ; nkeynes@254: idereg.status = 0x58; nkeynes@258: idereg.lba1 = blocksize & 0xFF; nkeynes@258: idereg.lba2 = blocksize >> 8; nkeynes@152: idereg.block_length = blocksize; nkeynes@152: idereg.block_left = blocksize; nkeynes@158: ide_raise_interrupt( ); nkeynes@152: } nkeynes@152: } nkeynes@152: nkeynes@152: static void ide_start_packet_read( int length, int blocksize ) nkeynes@152: { nkeynes@158: ide_set_packet_result( PKT_ERR_OK ); nkeynes@158: ide_start_read( length, blocksize, (idereg.feature & IDE_FEAT_DMA) ? TRUE : FALSE ); nkeynes@2: } nkeynes@2: nkeynes@125: static void ide_raise_interrupt( void ) nkeynes@2: { nkeynes@125: if( idereg.intrq_pending == 0 ) { nkeynes@125: idereg.intrq_pending = 1; nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) nkeynes@125: asic_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: } nkeynes@125: nkeynes@125: static void ide_clear_interrupt( void ) nkeynes@125: { nkeynes@125: if( idereg.intrq_pending != 0 ) { nkeynes@125: idereg.intrq_pending = 0; nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) nkeynes@125: asic_clear_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: } nkeynes@125: nkeynes@125: static void ide_set_error( int error_code ) nkeynes@125: { nkeynes@125: idereg.status = 0x51; nkeynes@125: idereg.error = error_code; nkeynes@2: } nkeynes@2: nkeynes@125: uint8_t ide_read_status( void ) nkeynes@125: { nkeynes@152: if( (idereg.status & IDE_STATUS_BSY) == 0 ) nkeynes@125: ide_clear_interrupt(); nkeynes@125: return idereg.status; nkeynes@2: } nkeynes@2: nkeynes@2: uint16_t ide_read_data_pio( void ) { nkeynes@152: if( idereg.state == IDE_STATE_PIO_READ ) { nkeynes@152: uint16_t rv = READ_BUFFER(); nkeynes@152: idereg.data_offset += 2; nkeynes@152: idereg.block_left -= 2; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.state = IDE_STATE_IDLE; nkeynes@152: idereg.status &= ~IDE_STATUS_DRQ; nkeynes@152: idereg.data_offset = -1; nkeynes@254: idereg.count = 3; /* complete */ nkeynes@152: ide_raise_interrupt(); nkeynes@152: } else if( idereg.block_left <= 0 ) { nkeynes@152: idereg.block_left = idereg.block_length; nkeynes@152: ide_raise_interrupt(); nkeynes@152: } nkeynes@152: return rv; nkeynes@152: } else { nkeynes@2: return 0xFFFF; nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@152: nkeynes@152: /** nkeynes@152: * DMA read request nkeynes@152: * nkeynes@152: * This method is called from the ASIC side when a DMA read request is nkeynes@152: * initiated. If there is a pending DMA transfer already, we copy the nkeynes@152: * data immediately, otherwise we record the DMA buffer for use when we nkeynes@152: * get to actually doing the transfer. nkeynes@152: * @return number of bytes transfered nkeynes@152: */ nkeynes@152: uint32_t ide_read_data_dma( uint32_t addr, uint32_t length ) nkeynes@152: { nkeynes@152: if( idereg.state == IDE_STATE_DMA_READ ) { nkeynes@152: int xferlen = length; nkeynes@152: int remaining = idereg.data_length - idereg.data_offset; nkeynes@152: if( xferlen > remaining ) nkeynes@152: xferlen = remaining; nkeynes@152: mem_copy_to_sh4( addr, data_buffer + idereg.data_offset, xferlen ); nkeynes@152: idereg.data_offset += xferlen; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.data_offset = -1; nkeynes@152: idereg.state = IDE_STATE_IDLE; nkeynes@158: idereg.status = 0x50; nkeynes@254: idereg.count = 0x03; nkeynes@152: ide_raise_interrupt(); nkeynes@158: asic_event( EVENT_IDE_DMA ); nkeynes@152: } nkeynes@152: return xferlen; nkeynes@152: } nkeynes@152: return 0; nkeynes@152: } nkeynes@152: nkeynes@2: void ide_write_data_pio( uint16_t val ) { nkeynes@152: if( idereg.state == IDE_STATE_CMD_WRITE ) { nkeynes@152: WRITE_BUFFER(val); nkeynes@152: idereg.data_offset+=2; nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.state = IDE_STATE_BUSY; nkeynes@152: idereg.status = (idereg.status & ~IDE_STATUS_DRQ) | IDE_STATUS_BSY; nkeynes@152: idereg.data_offset = -1; nkeynes@152: ide_packet_command(data_buffer); nkeynes@152: } nkeynes@152: } else if( idereg.state == IDE_STATE_PIO_WRITE ) { nkeynes@152: WRITE_BUFFER(val); nkeynes@152: if( idereg.data_offset >= idereg.data_length ) { nkeynes@152: idereg.state = IDE_STATE_BUSY; nkeynes@152: idereg.data_offset = -1; nkeynes@152: idereg.status = (idereg.status & ~IDE_STATUS_DRQ) | IDE_STATUS_BSY; nkeynes@152: /* ??? - no data writes yet anyway */ nkeynes@152: } nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@2: void ide_write_control( uint8_t val ) { nkeynes@125: if( IS_IDE_IRQ_ENABLED() ) { nkeynes@125: if( (val & 0x02) != 0 && idereg.intrq_pending != 0 ) nkeynes@125: asic_clear_event( EVENT_IDE ); nkeynes@125: } else { nkeynes@125: if( (val & 0x02) == 0 && idereg.intrq_pending != 0 ) nkeynes@125: asic_event( EVENT_IDE ); nkeynes@125: } nkeynes@125: idereg.control = val; nkeynes@2: } nkeynes@2: nkeynes@2: void ide_write_command( uint8_t val ) { nkeynes@125: ide_clear_interrupt(); nkeynes@2: idereg.command = val; nkeynes@2: switch( val ) { nkeynes@240: case IDE_CMD_NOP: /* Effectively an "abort" */ nkeynes@240: idereg.state = IDE_STATE_IDLE; nkeynes@240: idereg.status = 0x51; nkeynes@240: idereg.error = 0x04; nkeynes@240: idereg.data_offset = -1; nkeynes@240: ide_raise_interrupt(); nkeynes@240: return; nkeynes@39: case IDE_CMD_RESET_DEVICE: nkeynes@39: ide_reset(); nkeynes@39: break; nkeynes@39: case IDE_CMD_PACKET: nkeynes@152: ide_start_command_packet_write(); nkeynes@39: break; nkeynes@39: case IDE_CMD_SET_FEATURE: nkeynes@39: switch( idereg.feature ) { nkeynes@47: case IDE_FEAT_SET_TRANSFER_MODE: nkeynes@47: switch( idereg.count & 0xF8 ) { nkeynes@47: case IDE_XFER_PIO: nkeynes@47: INFO( "Set PIO default mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_PIO_FLOW: nkeynes@47: INFO( "Set PIO Flow-control mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_MULTI_DMA: nkeynes@47: INFO( "Set Multiword DMA mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: case IDE_XFER_ULTRA_DMA: nkeynes@47: INFO( "Set Ultra DMA mode: %d", idereg.count&0x07 ); nkeynes@47: break; nkeynes@47: default: nkeynes@47: INFO( "Setting unknown transfer mode: %02X", idereg.count ); nkeynes@47: break; nkeynes@47: } nkeynes@47: break; nkeynes@39: default: nkeynes@39: WARN( "IDE: unimplemented feature: %02X", idereg.feature ); nkeynes@39: } nkeynes@254: idereg.status = 0x50; nkeynes@254: idereg.error = 0x00; nkeynes@254: idereg.lba1 = 0x00; nkeynes@254: idereg.lba2 = 0x00; nkeynes@256: ide_raise_interrupt(); nkeynes@39: break; nkeynes@39: default: nkeynes@39: WARN( "IDE: Unimplemented command: %02X", val ); nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@142: /** nkeynes@142: * Execute a packet command. This particular method is responsible for parsing nkeynes@142: * the command buffers (12 bytes), and generating the appropriate responses, nkeynes@142: * although the actual implementation is mostly delegated to gdrom.c nkeynes@142: */ nkeynes@125: void ide_packet_command( unsigned char *cmd ) nkeynes@125: { nkeynes@138: uint32_t length, datalen; nkeynes@143: uint32_t lba, status; nkeynes@143: int mode; nkeynes@125: int blocksize = idereg.lba1 + (idereg.lba2<<8); nkeynes@125: nkeynes@125: /* Okay we have the packet in the command buffer */ nkeynes@166: INFO( "ATAPI packet: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", nkeynes@166: cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], nkeynes@166: cmd[8], cmd[9], cmd[10], cmd[11] ); nkeynes@254: nkeynes@254: if( cmd[0] != PKT_CMD_SENSE && idereg.was_reset ) { nkeynes@254: ide_set_packet_result( PKT_ERR_RESET ); nkeynes@254: idereg.was_reset = FALSE; nkeynes@254: return; nkeynes@254: } nkeynes@254: nkeynes@125: switch( cmd[0] ) { nkeynes@142: case PKT_CMD_TEST_READY: nkeynes@142: if( !gdrom_is_mounted() ) { nkeynes@152: ide_set_packet_result( PKT_ERR_NODISC ); nkeynes@158: } else { nkeynes@158: ide_set_packet_result( 0 ); nkeynes@158: ide_raise_interrupt(); nkeynes@158: idereg.status = 0x50; nkeynes@142: } nkeynes@142: break; nkeynes@125: case PKT_CMD_IDENTIFY: nkeynes@140: lba = cmd[2]; nkeynes@140: if( lba >= sizeof(gdrom_ident) ) { nkeynes@142: ide_set_error(PKT_ERR_BADFIELD); nkeynes@158: } else { nkeynes@158: length = cmd[4]; nkeynes@158: if( lba+length > sizeof(gdrom_ident) ) nkeynes@158: length = sizeof(gdrom_ident) - lba; nkeynes@158: memcpy( data_buffer, gdrom_ident + lba, length ); nkeynes@258: ide_start_packet_read( length, length ); nkeynes@140: } nkeynes@125: break; nkeynes@142: case PKT_CMD_SENSE: nkeynes@142: length = cmd[4]; nkeynes@142: if( length > 10 ) nkeynes@142: length = 10; nkeynes@152: memcpy( data_buffer, idereg.gdrom_sense, length ); nkeynes@258: ide_start_packet_read( length, length ); nkeynes@142: break; nkeynes@125: case PKT_CMD_READ_TOC: nkeynes@142: length = (cmd[3]<<8) | cmd[4]; nkeynes@142: if( length > sizeof(struct gdrom_toc) ) nkeynes@142: length = sizeof(struct gdrom_toc); nkeynes@152: nkeynes@152: status = gdrom_get_toc( data_buffer ); nkeynes@152: if( status != PKT_ERR_OK ) { nkeynes@152: ide_set_packet_result( status ); nkeynes@158: } else { nkeynes@258: ide_start_packet_read( length, length ); nkeynes@152: } nkeynes@125: break; nkeynes@158: case PKT_CMD_SESSION_INFO: nkeynes@149: length = cmd[4]; nkeynes@149: if( length > 6 ) nkeynes@149: length = 6; nkeynes@152: status = gdrom_get_info( data_buffer, cmd[2] ); nkeynes@152: if( status != PKT_ERR_OK ) { nkeynes@152: ide_set_packet_result( status ); nkeynes@158: } else { nkeynes@258: ide_start_packet_read( length, length ); nkeynes@152: } nkeynes@149: break; nkeynes@250: case PKT_CMD_PLAY_CD: nkeynes@250: ide_set_packet_result( 0 ); nkeynes@250: ide_raise_interrupt(); nkeynes@250: idereg.status = 0x50; nkeynes@250: break; nkeynes@125: case PKT_CMD_READ_SECTOR: nkeynes@125: lba = cmd[2] << 16 | cmd[3] << 8 | cmd[4]; nkeynes@125: length = cmd[8] << 16 | cmd[9] << 8 | cmd[10]; /* blocks */ nkeynes@143: switch( cmd[1] ) { nkeynes@245: case 0x20: mode = GDROM_MODE1; break; /* TODO - might be unchecked? */ nkeynes@143: case 0x24: mode = GDROM_GD; break; nkeynes@245: case 0x28: mode = GDROM_MODE2_XA1; break; /* ??? */ nkeynes@143: case 0x30: mode = GDROM_RAW; break; nkeynes@143: default: nkeynes@143: ERROR( "Unrecognized read mode '%02X' in GD-Rom read request", cmd[1] ); nkeynes@152: ide_set_packet_result( PKT_ERR_BADFIELD ); nkeynes@142: return; nkeynes@142: } nkeynes@158: nkeynes@158: if( length > data_buffer_len ) { nkeynes@158: do { nkeynes@158: data_buffer_len = data_buffer_len << 1; nkeynes@158: } while( data_buffer_len < length ); nkeynes@158: data_buffer = realloc( data_buffer, MAX_SECTOR_SIZE * data_buffer_len ); nkeynes@158: } nkeynes@158: nkeynes@152: datalen = data_buffer_len; nkeynes@152: status = gdrom_read_sectors( lba, length, mode, data_buffer, &datalen ); nkeynes@143: if( status != 0 ) { nkeynes@152: ide_set_packet_result( status ); nkeynes@152: idereg.gdrom_sense[5] = (lba >> 16) & 0xFF; nkeynes@152: idereg.gdrom_sense[6] = (lba >> 8) & 0xFF; nkeynes@152: idereg.gdrom_sense[7] = lba & 0xFF; nkeynes@258: WARN( " => Read CD returned sense key %02X, %02X", status & 0xFF, status >> 8 ); nkeynes@158: } else { nkeynes@245: idereg.last_read_lba = lba + length; nkeynes@245: idereg.last_read_track = gdrom_get_track_no_by_lba( idereg.last_read_lba ); nkeynes@258: ide_start_packet_read( datalen, 0x0800 ); nkeynes@125: } nkeynes@125: break; nkeynes@149: case PKT_CMD_SPIN_UP: nkeynes@149: /* do nothing? */ nkeynes@158: ide_set_packet_result( PKT_ERR_OK ); nkeynes@158: ide_raise_interrupt(); nkeynes@158: break; nkeynes@245: case PKT_CMD_STATUS: nkeynes@245: length = cmd[4]; nkeynes@245: if( !gdrom_is_mounted() ) { nkeynes@245: ide_set_packet_result( PKT_ERR_NODISC ); nkeynes@245: } else { nkeynes@245: switch( cmd[1] ) { nkeynes@245: case 0: nkeynes@250: if( length > sizeof(gdrom_status) ) { nkeynes@250: length = sizeof(gdrom_status); nkeynes@250: } nkeynes@250: memcpy( data_buffer, gdrom_status, length ); nkeynes@258: ide_start_packet_read( length, length ); nkeynes@245: break; nkeynes@245: case 1: nkeynes@254: if( length > 14 ) { nkeynes@254: length = 14; nkeynes@245: } nkeynes@254: gdrom_track_t track = gdrom_get_track(idereg.last_read_track); nkeynes@254: int offset = idereg.last_read_lba - track->lba; nkeynes@245: data_buffer[0] = 0x00; nkeynes@245: data_buffer[1] = 0x15; /* ??? */ nkeynes@245: data_buffer[2] = 0x00; nkeynes@245: data_buffer[3] = 0x0E; nkeynes@254: data_buffer[4] = track->flags; nkeynes@245: data_buffer[5] = idereg.last_read_track; nkeynes@245: data_buffer[6] = 0x01; /* ?? */ nkeynes@254: data_buffer[7] = (offset >> 16) & 0xFF; nkeynes@254: data_buffer[8] = (offset >> 8) & 0xFF; nkeynes@254: data_buffer[9] = offset & 0xFF; nkeynes@245: data_buffer[10] = (idereg.last_read_lba >> 24) & 0xFF; nkeynes@245: data_buffer[11] = (idereg.last_read_lba >> 16) & 0xFF; nkeynes@245: data_buffer[12] = (idereg.last_read_lba >> 8) & 0xFF; nkeynes@245: data_buffer[13] = idereg.last_read_lba & 0xFF; nkeynes@258: ide_start_packet_read( length, length ); nkeynes@245: break; nkeynes@245: } nkeynes@245: } nkeynes@245: break; nkeynes@158: case PKT_CMD_71: nkeynes@158: /* This is a weird one. As far as I can tell it returns random garbage nkeynes@158: * (and not even the same length each time, never mind the same data). nkeynes@158: * For sake of something to do, it returns the results from a test dump nkeynes@158: */ nkeynes@158: memcpy( data_buffer, gdrom_71, sizeof(gdrom_71) ); nkeynes@258: ide_start_packet_read( sizeof(gdrom_71), sizeof(gdrom_71) ); nkeynes@149: break; nkeynes@142: default: nkeynes@152: ide_set_packet_result( PKT_ERR_BADCMD ); /* Invalid command */ nkeynes@142: return; nkeynes@125: } nkeynes@125: }