Search
lxdream.org :: lxdream/src/drivers/cd_mmc.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cd_mmc.c
changeset 1023:264e2fd90be8
next1071:182cfe43c09e
author nkeynes
date Fri Jun 26 13:53:54 2009 +0000 (14 years ago)
permissions -rw-r--r--
last change Do the save-to-temp and rename thing when saving VMUs, for the sake of limiting corruption possibilities
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/drivers/cd_mmc.c Fri Jun 26 13:53:54 2009 +0000
1.3 @@ -0,0 +1,242 @@
1.4 +/**
1.5 + * $Id$
1.6 + *
1.7 + * SCSI/MMC device interface (depends on lower-level SCSI transport)
1.8 + *
1.9 + * Copyright (c) 2009 Nathan Keynes.
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify
1.12 + * it under the terms of the GNU General Public License as published by
1.13 + * the Free Software Foundation; either version 2 of the License, or
1.14 + * (at your option) any later version.
1.15 + *
1.16 + * This program is distributed in the hope that it will be useful,
1.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.19 + * GNU General Public License for more details.
1.20 + */
1.21 +
1.22 +#include <string.h>
1.23 +#include "lxdream.h"
1.24 +#include "gettext.h"
1.25 +#include "gdrom/gddriver.h"
1.26 +#include "gdrom/packet.h"
1.27 +
1.28 +#define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
1.29 +#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
1.30 +#define MAX_SECTORS_PER_CALL 1
1.31 +
1.32 +#define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES_PER_SECOND) + (m*CD_FRAMES_PER_MINUTE))
1.33 +
1.34 +static void mmc_make_read_cd_cmd( char *cmd, uint32_t real_sector, int mode )
1.35 +{
1.36 + cmd[0] = 0xBE;
1.37 + cmd[1] = (mode & 0x0E) << 1;
1.38 + cmd[2] = (real_sector >> 24) & 0xFF;
1.39 + cmd[3] = (real_sector >> 16) & 0xFF;
1.40 + cmd[4] = (real_sector >> 8) & 0xFF;
1.41 + cmd[5] = real_sector & 0xFF;
1.42 + cmd[6] = 0;
1.43 + cmd[7] = 0;
1.44 + cmd[8] = 1;
1.45 + cmd[9] = 0;
1.46 + cmd[10]= 0;
1.47 + cmd[11]= 0;
1.48 +
1.49 + if( READ_CD_RAW(mode) ) {
1.50 + cmd[9] = 0xF0;
1.51 + } else {
1.52 + if( READ_CD_HEADER(mode) ) {
1.53 + cmd[9] = 0xA0;
1.54 + }
1.55 + if( READ_CD_SUBHEAD(mode) ) {
1.56 + cmd[9] |= 0x40;
1.57 + }
1.58 + if( READ_CD_DATA(mode) ) {
1.59 + cmd[9] |= 0x10;
1.60 + }
1.61 + }
1.62 +}
1.63 +
1.64 +/**
1.65 + * Parse the TOC (format 2) into the gdrom_disc structure
1.66 + */
1.67 +void mmc_parse_toc2( gdrom_disc_t disc, unsigned char *buf )
1.68 +{
1.69 + int max_track = 0;
1.70 + int last_track = -1;
1.71 + int leadout = -1;
1.72 + int len = (buf[0] << 8) | buf[1];
1.73 + int session_type = -1;
1.74 + int i;
1.75 + for( i = 4; i<len; i+=11 ) {
1.76 + int session = buf[i];
1.77 + int adr = buf[i+1] >> 4;
1.78 + int point = buf[i+3];
1.79 + if( adr == 0x01 && point > 0 && point < 100 ) {
1.80 + /* Track info */
1.81 + int trackno = point-1;
1.82 + if( point > max_track ) {
1.83 + max_track = point;
1.84 + }
1.85 + disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
1.86 + disc->track[trackno].session = session - 1;
1.87 + disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
1.88 + if( disc->track[trackno].flags & TRACK_DATA ) {
1.89 + disc->track[trackno].mode = GDROM_MODE1;
1.90 + } else {
1.91 + disc->track[trackno].mode = GDROM_CDDA;
1.92 + }
1.93 + if( last_track != -1 ) {
1.94 + disc->track[last_track].sector_count = disc->track[trackno].lba -
1.95 + disc->track[last_track].lba;
1.96 + }
1.97 + last_track = trackno;
1.98 + } else switch( (adr << 8) | point ) {
1.99 + case 0x1A0: /* session info */
1.100 + if( buf[i+9] == 0x20 ) {
1.101 + session_type = IDE_DISC_CDROMXA;
1.102 + } else {
1.103 + session_type = IDE_DISC_CDROM;
1.104 + }
1.105 + disc->disc_type = session_type;
1.106 + break;
1.107 + case 0x1A2: /* leadout */
1.108 + leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
1.109 + break;
1.110 + }
1.111 + }
1.112 + disc->track_count = max_track;
1.113 +
1.114 + if( leadout != -1 && last_track != -1 ) {
1.115 + disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
1.116 + }
1.117 +}
1.118 +
1.119 +
1.120 +/**
1.121 + * Construct a drive indentification string based on the response to the
1.122 + * INQUIRY command. On success, the disc display_name is updated with the
1.123 + * drive name, otherwise the display_name is unchanged.
1.124 + * @return PKT_ERR_OK on success, otherwise the host failure code.
1.125 + */
1.126 +static gdrom_error_t gdrom_scsi_identify_drive( gdrom_disc_t disc )
1.127 +{
1.128 + unsigned char ident[256];
1.129 + uint32_t identlen = 256;
1.130 + char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0};
1.131 + gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, ident, &identlen );
1.132 + if( status == PKT_ERR_OK ) {
1.133 + char vendorid[9];
1.134 + char productid[17];
1.135 + char productrev[5];
1.136 + memcpy( vendorid, ident+8, 8 ); vendorid[8] = 0;
1.137 + memcpy( productid, ident+16, 16 ); productid[16] = 0;
1.138 + memcpy( productrev, ident+32, 4 ); productrev[4] = 0;
1.139 + g_free( (char *)disc->display_name );
1.140 + disc->display_name = g_strdup_printf( "%.8s %.16s %.4s", g_strstrip(vendorid),
1.141 + g_strstrip(productid), g_strstrip(productrev) );
1.142 + }
1.143 + return status;
1.144 +}
1.145 +
1.146 +
1.147 +static gdrom_error_t gdrom_scsi_read_sector( gdrom_disc_t disc, uint32_t sector,
1.148 + int mode, unsigned char *buf, uint32_t *length )
1.149 +{
1.150 + uint32_t real_sector = sector - GDROM_PREGAP;
1.151 + uint32_t sector_size = MAX_SECTOR_SIZE;
1.152 + char cmd[12];
1.153 +
1.154 + mmc_make_read_cd_cmd( cmd, real_sector, mode );
1.155 +
1.156 + gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, buf, &sector_size );
1.157 + if( status != 0 ) {
1.158 + return status;
1.159 + }
1.160 + /* FIXME */
1.161 + *length = 2048;
1.162 + return 0;
1.163 +}
1.164 +
1.165 +/**
1.166 + * Read the full table of contents into the disc from the device.
1.167 + */
1.168 +static gdrom_error_t gdrom_scsi_read_toc( gdrom_disc_t disc )
1.169 +{
1.170 + unsigned char buf[MAXTOCSIZE];
1.171 + uint32_t buflen = sizeof(buf);
1.172 + char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1.173 +
1.174 + cmd[7] = (sizeof(buf))>>8;
1.175 + cmd[8] = (sizeof(buf))&0xFF;
1.176 + memset( buf, 0, sizeof(buf) );
1.177 + gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read(disc, cmd, buf, &buflen );
1.178 + if( status == PKT_ERR_OK ) {
1.179 + mmc_parse_toc2( disc, buf );
1.180 + } else {
1.181 + if( status & 0xFF != 0x02 ) {
1.182 + /* Sense key 2 == Not Ready (ie temporary failure). Just ignore and
1.183 + * consider the drive empty for now, but warn about any other errors
1.184 + * we get. */
1.185 + WARN( _("Unable to read disc table of contents (error %04x)"), status );
1.186 + }
1.187 + disc->disc_type = IDE_DISC_NONE;
1.188 + }
1.189 + return status;
1.190 +}
1.191 +
1.192 +static gboolean gdrom_scsi_check_status( gdrom_disc_t disc )
1.193 +{
1.194 + if( SCSI_TRANSPORT(disc)->media_changed(disc) ) {
1.195 + gdrom_scsi_read_toc(disc);
1.196 + return TRUE;
1.197 + } else {
1.198 + return FALSE;
1.199 + }
1.200 +}
1.201 +
1.202 +static gdrom_error_t gdrom_scsi_play_audio( gdrom_disc_t disc, uint32_t lba, uint32_t endlba )
1.203 +{
1.204 + uint32_t real_sector = lba - GDROM_PREGAP;
1.205 + uint32_t length = endlba - lba;
1.206 +
1.207 + char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
1.208 + cmd[2] = (real_sector >> 24) & 0xFF;
1.209 + cmd[3] = (real_sector >> 16) & 0xFF;
1.210 + cmd[4] = (real_sector >> 8) & 0xFF;
1.211 + cmd[5] = real_sector & 0xFF;
1.212 + cmd[6] = (length >> 24) & 0xFF;
1.213 + cmd[7] = (length >> 16) & 0xFF;
1.214 + cmd[8] = (length >> 8) & 0xFF;
1.215 + cmd[9] = length & 0xFF;
1.216 +
1.217 + return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
1.218 +}
1.219 +
1.220 +
1.221 +gdrom_error_t gdrom_scsi_stop_audio( gdrom_disc_t disc )
1.222 +{
1.223 + int fd = fileno(disc->file);
1.224 + uint32_t buflen = 0;
1.225 + char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
1.226 +
1.227 + return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
1.228 +}
1.229 +
1.230 +
1.231 +gdrom_disc_t gdrom_scsi_disc_new( const gchar *filename, FILE *f, gdrom_scsi_transport_t scsi )
1.232 +{
1.233 + gdrom_disc_t disc = gdrom_disc_new(filename,f);
1.234 + if( disc != NULL ) {
1.235 + /* Initialize */
1.236 + disc->impl_data = scsi;
1.237 + disc->check_status = gdrom_scsi_check_status;
1.238 + disc->read_sector = gdrom_scsi_read_sector;
1.239 + disc->play_audio = gdrom_scsi_play_audio;
1.240 + disc->run_time_slice = NULL;
1.241 + gdrom_scsi_identify_drive(disc);
1.242 + gdrom_scsi_read_toc(disc);
1.243 + }
1.244 + return disc;
1.245 +}
1.246 \ No newline at end of file
.