nkeynes@31: /** nkeynes@480: * $Id: maple.c,v 1.12 2007-10-31 11:53:35 nkeynes Exp $ nkeynes@31: * nkeynes@31: * Implements the core Maple bus, including DMA transfers to and from the bus. nkeynes@31: * 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@31: */ nkeynes@35: #define MODULE maple_module nkeynes@31: nkeynes@1: #include nkeynes@480: #include nkeynes@1: #include "dream.h" nkeynes@1: #include "mem.h" nkeynes@1: #include "asic.h" nkeynes@1: #include "maple.h" nkeynes@1: nkeynes@15: void maple_init( void ); nkeynes@15: nkeynes@15: struct dreamcast_module maple_module = { "Maple", maple_init, NULL, NULL, NULL, nkeynes@23: NULL, NULL, NULL }; nkeynes@15: nkeynes@144: struct maple_device_class *maple_device_classes[] = { &controller_class, NULL }; nkeynes@144: nkeynes@15: void maple_init( void ) nkeynes@15: { nkeynes@15: nkeynes@15: } nkeynes@15: nkeynes@144: maple_device_t maple_new_device( const gchar *name ) nkeynes@144: { nkeynes@447: maple_device_class_t clz = maple_get_device_class(name); nkeynes@447: if( clz != NULL ) { nkeynes@447: return clz->new_device(); nkeynes@447: } nkeynes@447: return NULL; nkeynes@447: } nkeynes@447: nkeynes@480: maple_device_class_t maple_get_device_class( const gchar *name ) nkeynes@447: { nkeynes@144: int i; nkeynes@144: for( i=0; maple_device_classes[i] != NULL; i++ ) { nkeynes@144: if( g_strcasecmp(maple_device_classes[i]->name, name ) == 0 ) nkeynes@447: return maple_device_classes[i]; nkeynes@144: } nkeynes@144: return NULL; nkeynes@144: } nkeynes@144: nkeynes@447: const struct maple_device_class **maple_get_device_classes() nkeynes@447: { nkeynes@480: return (const struct maple_device_class **)maple_device_classes; nkeynes@447: } nkeynes@447: nkeynes@450: lxdream_config_entry_t maple_get_device_config( maple_device_t dev ) nkeynes@144: { nkeynes@144: if( dev->get_config == NULL ) nkeynes@144: return NULL; nkeynes@144: return dev->get_config(dev); nkeynes@144: } nkeynes@144: nkeynes@2: /** nkeynes@2: * Input data looks like this: nkeynes@2: * 0: transfer control word nkeynes@2: * 0: length of data in words (not including 3 word header) nkeynes@2: * 1: low bit = lightgun mode nkeynes@2: * 2: low 2 bits = port # (0..3) nkeynes@2: * 3: 0x80 = last packet, 0x00 = normal packet nkeynes@2: * 4: output buffer address nkeynes@2: * 8: Command word nkeynes@2: * 8: command code nkeynes@2: * 9: destination address nkeynes@2: * 10: source address nkeynes@2: * 11: length of data in words (not including 3 word header) nkeynes@2: * 12: command-specific data nkeynes@2: */ nkeynes@2: nkeynes@2: /** nkeynes@2: * array is [port][subperipheral], so [0][0] is main peripheral on port A, nkeynes@2: * [1][2] is the second subperipheral on port B and so on. nkeynes@2: */ nkeynes@2: maple_device_t maple_devices[4][6]; nkeynes@2: int maple_periph_mask[4]; nkeynes@2: #define GETBYTE(n) ((uint32_t)(buf[n])) nkeynes@2: #define GETWORD(n) (*((uint32_t *)(buf+(n)))) nkeynes@2: #define PUTBYTE(n,x) (buf[n] = (char)x) nkeynes@2: #define PUTWORD(n,x) (*((uint32_t *)(return_buf+(n))) = (x)) nkeynes@2: nkeynes@144: maple_device_t maple_get_device( unsigned int port, unsigned int periph ) { nkeynes@144: if( port >= 4 ) nkeynes@144: return NULL; nkeynes@144: if( periph >= 6 ) nkeynes@144: return NULL; nkeynes@144: return maple_devices[port][periph]; nkeynes@144: } nkeynes@144: nkeynes@1: void maple_handle_buffer( uint32_t address ) { nkeynes@2: unsigned char *buf = (unsigned char *)mem_get_region(address); nkeynes@1: if( buf == NULL ) { nkeynes@1: ERROR( "Invalid or unmapped buffer passed to maple (0x%08X)", address ); nkeynes@1: } else { nkeynes@2: unsigned int last = 0; nkeynes@2: int i = 0, count; nkeynes@2: for( count=0; !last; count++ ) { nkeynes@2: unsigned int port, length, gun, periph, periph_id, out_length; nkeynes@2: unsigned int cmd, recv_addr, send_addr; nkeynes@2: uint32_t return_addr; nkeynes@2: unsigned char *return_buf; nkeynes@1: nkeynes@2: last = GETBYTE(3) & 0x80; /* indicates last packet */ nkeynes@2: port = GETBYTE(2) & 0x03; nkeynes@2: gun = GETBYTE(1) & 0x01; nkeynes@2: length = GETBYTE(0) & 0xFF; nkeynes@2: return_addr = GETWORD(4); nkeynes@447: nkeynes@447: if( (return_addr & 0x1C000000) != 0x0C000000 ) { nkeynes@447: ERROR( "Bad return address in maple packet: %08X", return_addr ); nkeynes@447: break; nkeynes@2: } nkeynes@2: return_buf = mem_get_region(return_addr); nkeynes@2: cmd = GETBYTE(8); nkeynes@2: recv_addr = GETBYTE(9); nkeynes@2: send_addr = GETBYTE(10); nkeynes@2: /* Sanity checks */ nkeynes@2: if( GETBYTE(11) != length || nkeynes@447: send_addr >> 6 != port || nkeynes@2: recv_addr >> 6 != port || nkeynes@2: return_buf == NULL ) { nkeynes@447: ERROR( "Received bad packet: %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", nkeynes@447: buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], nkeynes@447: buf[8], buf[9], buf[10], buf[11] ); nkeynes@447: break; nkeynes@2: } nkeynes@2: periph = 0; nkeynes@2: periph_id = recv_addr & 0x3F; nkeynes@2: if( periph_id != 0x20 ) { nkeynes@2: for( i=0;i<5;i++ ) { nkeynes@2: if( periph_id == (1<ident, 112 ); nkeynes@2: out_length = 0x1C; nkeynes@2: break; nkeynes@2: case MAPLE_CMD_EXT_INFO: nkeynes@2: status = MAPLE_RESP_EXT_INFO; nkeynes@2: memcpy( return_buf+4, dev->ident, 192 ); nkeynes@2: out_length = 0x30; nkeynes@2: break; nkeynes@2: case MAPLE_CMD_RESET: nkeynes@2: if( dev->reset == NULL ) nkeynes@2: status = MAPLE_RESP_ACK; nkeynes@2: else status = dev->reset(dev); nkeynes@2: break; nkeynes@2: case MAPLE_CMD_SHUTDOWN: nkeynes@2: if( dev->shutdown == NULL ) nkeynes@2: status = MAPLE_RESP_ACK; nkeynes@2: else status = dev->shutdown(dev); nkeynes@2: break; nkeynes@2: case MAPLE_CMD_GET_COND: nkeynes@2: func = GETWORD(12); nkeynes@2: if( dev->get_condition == NULL ) nkeynes@2: status = MAPLE_ERR_CMD_UNKNOWN; nkeynes@2: else status = dev->get_condition(dev, func, nkeynes@2: return_buf+8, nkeynes@2: &out_length ); nkeynes@121: out_length++; nkeynes@2: if( status == 0 ) { nkeynes@2: status = MAPLE_RESP_DATA; nkeynes@2: PUTWORD(4,func); nkeynes@2: } nkeynes@2: break; nkeynes@2: case MAPLE_CMD_SET_COND: nkeynes@2: func = GETWORD(12); nkeynes@2: if( dev->set_condition == NULL ) nkeynes@2: status = MAPLE_ERR_CMD_UNKNOWN; nkeynes@2: else status = dev->set_condition(dev, func, nkeynes@2: buf+16, nkeynes@2: length); nkeynes@2: if( status == 0 ) nkeynes@2: status = MAPLE_RESP_ACK; nkeynes@2: break; nkeynes@2: case MAPLE_CMD_READ_BLOCK: nkeynes@2: func = GETWORD(12); nkeynes@2: block = GETWORD(16); nkeynes@2: if( dev->read_block == NULL ) nkeynes@2: status = MAPLE_ERR_CMD_UNKNOWN; nkeynes@2: else status = dev->read_block(dev, func, block, nkeynes@2: return_buf+12, nkeynes@2: &out_length ); nkeynes@2: if( status == 0 ) { nkeynes@2: status = MAPLE_RESP_DATA; nkeynes@2: PUTWORD(4,func); nkeynes@2: PUTWORD(8,block); nkeynes@2: } nkeynes@2: break; nkeynes@2: case MAPLE_CMD_WRITE_BLOCK: nkeynes@2: func = GETWORD(12); nkeynes@2: block = GETWORD(16); nkeynes@2: if( dev->write_block == NULL ) nkeynes@2: status = MAPLE_ERR_CMD_UNKNOWN; nkeynes@2: else { nkeynes@2: status = dev->write_block(dev, func, block, nkeynes@2: buf+20, length); nkeynes@2: if( status == 0 ) nkeynes@2: status = MAPLE_RESP_ACK; nkeynes@2: } nkeynes@2: break; nkeynes@2: default: nkeynes@2: status = MAPLE_ERR_CMD_UNKNOWN; nkeynes@2: } nkeynes@2: return_buf[0] = status; nkeynes@2: return_buf[1] = send_addr; nkeynes@2: return_buf[2] = recv_addr; nkeynes@2: if( periph == 0 ) nkeynes@2: return_buf[2] |= maple_periph_mask[port]; nkeynes@2: return_buf[3] = out_length; nkeynes@2: } nkeynes@2: buf += 12 + (length<<2); nkeynes@447: address += 12 + (length<<2); nkeynes@2: } nkeynes@2: asic_event( EVENT_MAPLE_DMA ); nkeynes@1: } nkeynes@1: } nkeynes@2: nkeynes@2: void maple_attach_device( maple_device_t dev, unsigned int port, nkeynes@2: unsigned int periph ) { nkeynes@2: assert( port < 4 ); nkeynes@2: assert( periph < 6 ); nkeynes@2: nkeynes@2: if( maple_devices[port][periph] != NULL ) { nkeynes@2: /* Detach existing peripheral first */ nkeynes@2: maple_detach_device( port, periph ); nkeynes@2: } nkeynes@2: nkeynes@2: maple_devices[port][periph] = dev; nkeynes@2: if( periph != 0 ) nkeynes@2: maple_periph_mask[port] |= (1<<(periph-1)); nkeynes@2: else maple_periph_mask[port] |= 0x20; nkeynes@2: if( dev->attach != NULL ) { nkeynes@2: dev->attach( dev ); nkeynes@2: } nkeynes@2: } nkeynes@2: nkeynes@2: void maple_detach_device( unsigned int port, unsigned int periph ) { nkeynes@2: assert( port < 4 ); nkeynes@2: assert( periph < 6 ); nkeynes@2: nkeynes@2: maple_device_t dev = maple_devices[port][periph]; nkeynes@2: if( dev == NULL ) /* already detached */ nkeynes@2: return; nkeynes@2: maple_devices[port][periph] = NULL; nkeynes@2: if( dev->detach != NULL ) { nkeynes@2: dev->detach(dev); nkeynes@2: } nkeynes@447: if( dev->destroy != NULL ) { nkeynes@447: dev->destroy(dev); nkeynes@447: } nkeynes@2: if( periph == 0 ) { nkeynes@2: /* If we detach the main peripheral, we also have to detach all the nkeynes@2: * subperipherals, or the system could get quite confused nkeynes@2: */ nkeynes@2: int i; nkeynes@2: maple_periph_mask[port] = 0; nkeynes@2: for( i=1; i<6; i++ ) { nkeynes@2: maple_detach_device(port,i); nkeynes@2: } nkeynes@2: } else { nkeynes@2: maple_periph_mask[port] &= (~(1<<(periph-1))); nkeynes@2: } nkeynes@144: nkeynes@2: } nkeynes@144: nkeynes@144: void maple_detach_all() { nkeynes@144: int i, j; nkeynes@144: for( i=0; i<4; i++ ) { nkeynes@144: for( j=0; j<6; j++ ) { nkeynes@144: if( maple_devices[i][j] != NULL ) { nkeynes@144: maple_device_t dev = maple_devices[i][j]; nkeynes@144: if( dev->detach != NULL ) nkeynes@144: dev->detach(dev); nkeynes@144: if( dev->destroy != NULL ) nkeynes@144: dev->destroy(dev); nkeynes@144: } nkeynes@144: } nkeynes@144: maple_periph_mask[i] = 0; nkeynes@144: } nkeynes@144: } nkeynes@144: nkeynes@144: void maple_reattach_all() { nkeynes@144: int i, j; nkeynes@144: for( i=0; i<4; i++ ) { nkeynes@144: for( j=0; j<6; j++ ) { nkeynes@144: if( maple_devices[i][j] != NULL ) { nkeynes@144: maple_device_t dev = maple_devices[i][j]; nkeynes@144: if( dev->attach != NULL ) nkeynes@144: dev->attach(dev); nkeynes@144: } nkeynes@144: } nkeynes@144: } nkeynes@144: }