nkeynes@1034 | 1 | /**
|
nkeynes@1035 | 2 | * $Id$
|
nkeynes@1034 | 3 | *
|
nkeynes@1034 | 4 | * Implementation of the SEGA VMU device
|
nkeynes@1034 | 5 | * Part No. HKT-7000
|
nkeynes@1034 | 6 | *
|
nkeynes@1034 | 7 | * The standard VMU implements 3 functions - Clock, LCD, and memory card,
|
nkeynes@1034 | 8 | * in addition to having it's own little CPU, buttons, etc. The CPU isn't
|
nkeynes@1034 | 9 | * implemented just yet.
|
nkeynes@1034 | 10 | *
|
nkeynes@1034 | 11 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@1034 | 12 | *
|
nkeynes@1034 | 13 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1034 | 14 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1034 | 15 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1034 | 16 | * (at your option) any later version.
|
nkeynes@1034 | 17 | *
|
nkeynes@1034 | 18 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1034 | 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1034 | 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1034 | 21 | * GNU General Public License for more details.
|
nkeynes@1034 | 22 | */
|
nkeynes@1034 | 23 |
|
nkeynes@1034 | 24 | #include <assert.h>
|
nkeynes@1034 | 25 | #include <stdlib.h>
|
nkeynes@1034 | 26 | #include <stdio.h>
|
nkeynes@1034 | 27 | #include <string.h>
|
nkeynes@1034 | 28 | #include <errno.h>
|
nkeynes@1034 | 29 | #include <sys/mman.h>
|
nkeynes@1034 | 30 | #include <fcntl.h>
|
nkeynes@1034 | 31 | #include "display.h"
|
nkeynes@1034 | 32 | #include "maple/maple.h"
|
nkeynes@1034 | 33 | #include "vmu/vmuvol.h"
|
nkeynes@1034 | 34 | #include "vmu/vmulist.h"
|
nkeynes@1034 | 35 |
|
nkeynes@1034 | 36 | #define VMU_LCD_SIZE (48*4)
|
nkeynes@1034 | 37 | #define VMU_BLOCK_COUNT 256
|
nkeynes@1034 | 38 | #define VMU_BLOCK_SIZE 512
|
nkeynes@1034 | 39 | #define VMU_PHASE_SIZE 128
|
nkeynes@1034 | 40 | #define VMU_CONFIG_ENTRIES 1
|
nkeynes@1034 | 41 |
|
nkeynes@1034 | 42 | #define VMU_IDENT { 0x00, 0x00, 0x00, 0x0E, 0x7E, 0x7E, 0x3F, 0x40, 0x00, 0x05, 0x10, 0x00, \
|
nkeynes@1034 | 43 | 0x00, 0x0F, 0x41, 0x00, 0xFF, 0x00, 0x56, 0x69, 0x73, 0x75, 0x61, 0x6C, 0x20, 0x4D, 0x65, 0x6D, \
|
nkeynes@1034 | 44 | 0x6F, 0x72, 0x79, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, \
|
nkeynes@1034 | 45 | 0x20, 0x20, 0x20, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x42, 0x79, 0x20, \
|
nkeynes@1034 | 46 | 0x6F, 0x72, 0x20, 0x55, 0x6E, 0x64, 0x65, 0x72, 0x20, 0x4C, 0x69, 0x63, 0x65, 0x6E, 0x73, 0x65, \
|
nkeynes@1034 | 47 | 0x20, 0x46, 0x72, 0x6F, 0x6D, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x45, 0x4E, 0x54, 0x45, 0x52, \
|
nkeynes@1034 | 48 | 0x50, 0x52, 0x49, 0x53, 0x45, 0x53, 0x2C, 0x4C, 0x54, 0x44, 0x2E, 0x20, 0x20, 0x20, 0x20, 0x20, \
|
nkeynes@1034 | 49 | 0x7C, 0x00, 0x82, 0x00 }
|
nkeynes@1034 | 50 | #define VMU_VERSION { 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x20, 0x31, 0x2E, 0x30, 0x30, \
|
nkeynes@1034 | 51 | 0x35, 0x2C, 0x31, 0x39, 0x39, 0x39, 0x2F, 0x30, 0x34, 0x2F, 0x31, 0x35, 0x2C, 0x33, 0x31, 0x35, \
|
nkeynes@1034 | 52 | 0x2D, 0x36, 0x32, 0x30, 0x38, 0x2D, 0x30, 0x33, 0x2C, 0x53, 0x45, 0x47, 0x41, 0x20, 0x56, 0x69, \
|
nkeynes@1034 | 53 | 0x73, 0x75, 0x61, 0x6C, 0x20, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0x20, 0x53, 0x79, 0x73, 0x74, \
|
nkeynes@1034 | 54 | 0x65, 0x6D, 0x20, 0x42, 0x49, 0x4F, 0x53, 0x20, 0x50, 0x72, 0x6F, 0x64, 0x75, 0x63, 0x65, 0x64, \
|
nkeynes@1034 | 55 | 0x20, 0x62, 0x79, 0x20 }
|
nkeynes@1034 | 56 |
|
nkeynes@1034 | 57 | static void vmu_destroy( maple_device_t dev );
|
nkeynes@1034 | 58 | static maple_device_t vmu_clone( maple_device_t dev );
|
nkeynes@1034 | 59 | static maple_device_t vmu_new();
|
nkeynes@1072 | 60 | static lxdream_config_group_t vmu_get_config( maple_device_t dev );
|
nkeynes@1072 | 61 | static gboolean vmu_set_config_value( void *data, lxdream_config_group_t group, unsigned int key,
|
nkeynes@1072 | 62 | const gchar *oldvalue, const gchar *value );
|
nkeynes@1034 | 63 | static int vmu_get_condition( maple_device_t dev, int function, unsigned char *outbuf,
|
nkeynes@1034 | 64 | unsigned int *outlen );
|
nkeynes@1034 | 65 | static int vmu_get_meminfo( maple_device_t dev, int function, unsigned int pt,
|
nkeynes@1034 | 66 | unsigned char *outbuf, unsigned int *outlen );
|
nkeynes@1034 | 67 | static void vmu_attach(struct maple_device *dev);
|
nkeynes@1034 | 68 | static void vmu_detach(struct maple_device *dev);
|
nkeynes@1034 | 69 | static int vmu_read_block(struct maple_device *dev, int function, unsigned int pt,
|
nkeynes@1034 | 70 | uint32_t block, unsigned int phase,
|
nkeynes@1034 | 71 | unsigned char *outbuf, unsigned int *buflen);
|
nkeynes@1034 | 72 | static int vmu_write_block(struct maple_device *dev, int function, unsigned int pt,
|
nkeynes@1034 | 73 | uint32_t block, unsigned int phase,
|
nkeynes@1034 | 74 | unsigned char *inbuf, unsigned int buflen);
|
nkeynes@1034 | 75 |
|
nkeynes@1034 | 76 | typedef struct vmu_device {
|
nkeynes@1034 | 77 | struct maple_device dev;
|
nkeynes@1034 | 78 | vmu_volume_t vol;
|
nkeynes@1034 | 79 | char lcd_bitmap[VMU_LCD_SIZE]; /* 48x32 bitmap */
|
nkeynes@1072 | 80 | struct lxdream_config_group config;
|
nkeynes@1034 | 81 | } *vmu_device_t;
|
nkeynes@1034 | 82 |
|
nkeynes@1072 | 83 | #define DEV_FROM_CONFIG_GROUP(grp) ((vmu_device_t)(((char *)grp) - offsetof( struct vmu_device, config )))
|
nkeynes@1072 | 84 |
|
nkeynes@1034 | 85 | struct maple_device_class vmu_class = { "Sega VMU", MAPLE_GRAB_DONTCARE, vmu_new };
|
nkeynes@1034 | 86 |
|
nkeynes@1034 | 87 | static struct vmu_device base_vmu = {
|
nkeynes@1034 | 88 | { MAPLE_DEVICE_TAG, &vmu_class,
|
nkeynes@1034 | 89 | VMU_IDENT, VMU_VERSION,
|
nkeynes@1072 | 90 | vmu_get_config,
|
nkeynes@1034 | 91 | vmu_attach, vmu_detach, vmu_destroy,
|
nkeynes@1034 | 92 | vmu_clone, NULL, NULL, vmu_get_condition, NULL,
|
nkeynes@1034 | 93 | vmu_get_meminfo, vmu_read_block, vmu_write_block, NULL, NULL },
|
nkeynes@1072 | 94 | NULL, {0},
|
nkeynes@1072 | 95 | {"Sega VMU", vmu_set_config_value, NULL, NULL,
|
nkeynes@1034 | 96 | {{ "volume", N_("Volume"), CONFIG_TYPE_FILE },
|
nkeynes@1072 | 97 | { NULL, CONFIG_TYPE_NONE }}} };
|
nkeynes@1072 | 98 |
|
nkeynes@1072 | 99 |
|
nkeynes@1034 | 100 |
|
nkeynes@1034 | 101 | static maple_device_t vmu_new( )
|
nkeynes@1034 | 102 | {
|
nkeynes@1034 | 103 | vmu_device_t dev = malloc( sizeof(struct vmu_device) );
|
nkeynes@1088 | 104 | memcpy( dev, &base_vmu, sizeof(base_vmu) );
|
nkeynes@1072 | 105 | dev->config.data = dev;
|
nkeynes@1034 | 106 | return MAPLE_DEVICE(dev);
|
nkeynes@1034 | 107 | }
|
nkeynes@1034 | 108 |
|
nkeynes@1034 | 109 | static maple_device_t vmu_clone( maple_device_t srcdevice )
|
nkeynes@1034 | 110 | {
|
nkeynes@1034 | 111 | vmu_device_t src = (vmu_device_t)srcdevice;
|
nkeynes@1034 | 112 | vmu_device_t dev = (vmu_device_t)vmu_new();
|
nkeynes@1072 | 113 | lxdream_copy_config_group( &dev->config, &src->config );
|
nkeynes@1072 | 114 | dev->config.data = dev;
|
nkeynes@1034 | 115 | return MAPLE_DEVICE(dev);
|
nkeynes@1034 | 116 | }
|
nkeynes@1034 | 117 |
|
nkeynes@1072 | 118 | static lxdream_config_group_t vmu_get_config( maple_device_t mdev )
|
nkeynes@1034 | 119 | {
|
nkeynes@1034 | 120 | vmu_device_t dev = (vmu_device_t)mdev;
|
nkeynes@1072 | 121 | return &dev->config;
|
nkeynes@1034 | 122 | }
|
nkeynes@1034 | 123 |
|
nkeynes@1072 | 124 | static gboolean vmu_set_config_value( void *data, lxdream_config_group_t group, unsigned int key,
|
nkeynes@1072 | 125 | const gchar *oldvalue, const gchar *value )
|
nkeynes@1034 | 126 | {
|
nkeynes@1072 | 127 | vmu_device_t vmu = (vmu_device_t)data;
|
nkeynes@1034 | 128 | assert( key < VMU_CONFIG_ENTRIES );
|
nkeynes@1034 | 129 |
|
nkeynes@1034 | 130 | if( vmu->vol != NULL ) {
|
nkeynes@1034 | 131 | vmulist_detach_vmu(vmu->vol);
|
nkeynes@1034 | 132 | }
|
nkeynes@1034 | 133 | vmu->vol = vmulist_get_vmu_by_filename( value );
|
nkeynes@1034 | 134 | if( vmu->vol != NULL ) {
|
nkeynes@1034 | 135 | vmulist_attach_vmu(vmu->vol, "MAPLE");
|
nkeynes@1034 | 136 | }
|
nkeynes@1072 | 137 | return TRUE;
|
nkeynes@1034 | 138 | }
|
nkeynes@1034 | 139 |
|
nkeynes@1034 | 140 | void vmu_attach(struct maple_device *dev)
|
nkeynes@1034 | 141 | {
|
nkeynes@1034 | 142 | vmu_device_t vmu = (vmu_device_t)dev;
|
nkeynes@1072 | 143 | if( vmu->config.params[0].value != NULL ) {
|
nkeynes@1072 | 144 | vmu->vol = vmulist_get_vmu_by_filename(vmu->config.params[0].value);
|
nkeynes@1034 | 145 | if( vmu->vol != NULL ) {
|
nkeynes@1034 | 146 | vmulist_attach_vmu(vmu->vol, "MAPLE");
|
nkeynes@1034 | 147 | }
|
nkeynes@1034 | 148 | }
|
nkeynes@1034 | 149 | }
|
nkeynes@1034 | 150 |
|
nkeynes@1034 | 151 | static void vmu_detach(struct maple_device *dev)
|
nkeynes@1034 | 152 | {
|
nkeynes@1034 | 153 | vmu_device_t vmu = (vmu_device_t)dev;
|
nkeynes@1034 | 154 | if( vmu->vol != NULL ) {
|
nkeynes@1034 | 155 | vmulist_detach_vmu(vmu->vol);
|
nkeynes@1034 | 156 | vmu->vol = NULL;
|
nkeynes@1034 | 157 | }
|
nkeynes@1034 | 158 | }
|
nkeynes@1034 | 159 |
|
nkeynes@1034 | 160 | static void vmu_destroy( maple_device_t dev )
|
nkeynes@1034 | 161 | {
|
nkeynes@1034 | 162 | vmu_device_t vmu = (vmu_device_t)dev;
|
nkeynes@1034 | 163 | free( dev );
|
nkeynes@1034 | 164 | }
|
nkeynes@1034 | 165 |
|
nkeynes@1034 | 166 | static int vmu_get_condition(struct maple_device *dev, int function,
|
nkeynes@1034 | 167 | unsigned char *outbuf, unsigned int *buflen)
|
nkeynes@1034 | 168 | {
|
nkeynes@1072 | 169 | return 0;
|
nkeynes@1034 | 170 | }
|
nkeynes@1034 | 171 | static int vmu_set_condition(struct maple_device *dev, int function,
|
nkeynes@1034 | 172 | unsigned char *inbuf, unsigned int buflen)
|
nkeynes@1034 | 173 | {
|
nkeynes@1034 | 174 | return MAPLE_ERR_NO_RESPONSE; /* CHECKME */
|
nkeynes@1034 | 175 | }
|
nkeynes@1034 | 176 |
|
nkeynes@1034 | 177 | static int vmu_get_meminfo(struct maple_device *dev, int function, unsigned int pt,
|
nkeynes@1034 | 178 | unsigned char *outbuf, unsigned int *buflen)
|
nkeynes@1034 | 179 | {
|
nkeynes@1034 | 180 | struct vmu_device *vmu = (struct vmu_device *)dev;
|
nkeynes@1034 | 181 | switch(function) {
|
nkeynes@1034 | 182 | case MAPLE_FUNC_MEMORY:
|
nkeynes@1034 | 183 | if( vmu->vol != NULL ) {
|
nkeynes@1034 | 184 | const struct vmu_volume_metadata *md = vmu_volume_get_metadata( vmu->vol, pt );
|
nkeynes@1034 | 185 | memcpy( outbuf, md, sizeof(struct vmu_volume_metadata) );
|
nkeynes@1034 | 186 | *buflen = sizeof(struct vmu_volume_metadata);
|
nkeynes@1034 | 187 | return 0;
|
nkeynes@1034 | 188 | } // Else fallthrough
|
nkeynes@1034 | 189 | case MAPLE_FUNC_LCD:
|
nkeynes@1034 | 190 | case MAPLE_FUNC_CLOCK:
|
nkeynes@1034 | 191 | return MAPLE_ERR_NO_RESPONSE;
|
nkeynes@1034 | 192 | default:
|
nkeynes@1034 | 193 | return MAPLE_ERR_FUNC_UNSUP;
|
nkeynes@1034 | 194 | }
|
nkeynes@1034 | 195 |
|
nkeynes@1034 | 196 | }
|
nkeynes@1034 | 197 | static int vmu_read_block(struct maple_device *dev, int function, unsigned int pt,
|
nkeynes@1034 | 198 | uint32_t block, unsigned int phase,
|
nkeynes@1034 | 199 | unsigned char *outbuf, unsigned int *buflen)
|
nkeynes@1034 | 200 | {
|
nkeynes@1034 | 201 | struct vmu_device *vmu = (struct vmu_device *)dev;
|
nkeynes@1034 | 202 | switch( function ) {
|
nkeynes@1034 | 203 | case MAPLE_FUNC_LCD:
|
nkeynes@1034 | 204 | if( pt == 0 && block == 0 ) {
|
nkeynes@1034 | 205 | *buflen = VMU_LCD_SIZE/4;
|
nkeynes@1034 | 206 | memcpy( outbuf, vmu->lcd_bitmap, VMU_LCD_SIZE/4 );
|
nkeynes@1034 | 207 | }
|
nkeynes@1034 | 208 | return 0;
|
nkeynes@1034 | 209 | break;
|
nkeynes@1034 | 210 | case MAPLE_FUNC_MEMORY:
|
nkeynes@1034 | 211 | if( vmu->vol != NULL ) {
|
nkeynes@1172 | 212 | if( vmu_volume_read_block( vmu->vol, pt, block, outbuf ) ) {
|
nkeynes@1172 | 213 | *buflen = VMU_BLOCK_SIZE/4;
|
nkeynes@1172 | 214 | }
|
nkeynes@1034 | 215 | return 0;
|
nkeynes@1034 | 216 | }
|
nkeynes@1034 | 217 | // Else fallthrough for now
|
nkeynes@1034 | 218 | case MAPLE_FUNC_CLOCK:
|
nkeynes@1034 | 219 | return MAPLE_ERR_NO_RESPONSE; /* CHECKME */
|
nkeynes@1034 | 220 | default:
|
nkeynes@1034 | 221 | return MAPLE_ERR_FUNC_UNSUP;
|
nkeynes@1034 | 222 | }
|
nkeynes@1034 | 223 | }
|
nkeynes@1034 | 224 |
|
nkeynes@1034 | 225 | static int vmu_write_block(struct maple_device *dev, int function, unsigned int pt,
|
nkeynes@1034 | 226 | uint32_t block, unsigned int phase,
|
nkeynes@1034 | 227 | unsigned char *inbuf, unsigned int buflen)
|
nkeynes@1034 | 228 | {
|
nkeynes@1034 | 229 | struct vmu_device *vmu = (struct vmu_device *)dev;
|
nkeynes@1034 | 230 | switch( function ) {
|
nkeynes@1034 | 231 | case MAPLE_FUNC_LCD:
|
nkeynes@1034 | 232 | if( pt == 0 && block == 0 && buflen == (VMU_LCD_SIZE/4) ) {
|
nkeynes@1034 | 233 | memcpy( vmu->lcd_bitmap, inbuf, VMU_LCD_SIZE );
|
nkeynes@1034 | 234 | }
|
nkeynes@1034 | 235 | return 0;
|
nkeynes@1034 | 236 | break;
|
nkeynes@1034 | 237 | case MAPLE_FUNC_MEMORY:
|
nkeynes@1034 | 238 | if( vmu->vol != NULL && buflen == (VMU_PHASE_SIZE/4) ) {
|
nkeynes@1034 | 239 | vmu_volume_write_phase( vmu->vol, pt, block, phase, inbuf );
|
nkeynes@1034 | 240 | return 0;
|
nkeynes@1034 | 241 | }
|
nkeynes@1034 | 242 | // Else fallthrough for now
|
nkeynes@1034 | 243 | case MAPLE_FUNC_CLOCK:
|
nkeynes@1034 | 244 | return MAPLE_ERR_NO_RESPONSE; /* CHECKME */
|
nkeynes@1034 | 245 | default:
|
nkeynes@1034 | 246 | return MAPLE_ERR_FUNC_UNSUP;
|
nkeynes@1034 | 247 | }
|
nkeynes@1034 | 248 | }
|