nkeynes@1023 | 1 | /**
|
nkeynes@1023 | 2 | * $Id$
|
nkeynes@1023 | 3 | *
|
nkeynes@1023 | 4 | * SCSI/MMC device interface (depends on lower-level SCSI transport)
|
nkeynes@1023 | 5 | *
|
nkeynes@1023 | 6 | * Copyright (c) 2009 Nathan Keynes.
|
nkeynes@1023 | 7 | *
|
nkeynes@1023 | 8 | * This program is free software; you can redistribute it and/or modify
|
nkeynes@1023 | 9 | * it under the terms of the GNU General Public License as published by
|
nkeynes@1023 | 10 | * the Free Software Foundation; either version 2 of the License, or
|
nkeynes@1023 | 11 | * (at your option) any later version.
|
nkeynes@1023 | 12 | *
|
nkeynes@1023 | 13 | * This program is distributed in the hope that it will be useful,
|
nkeynes@1023 | 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
nkeynes@1023 | 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
nkeynes@1023 | 16 | * GNU General Public License for more details.
|
nkeynes@1023 | 17 | */
|
nkeynes@1023 | 18 |
|
nkeynes@1023 | 19 | #include <string.h>
|
nkeynes@1023 | 20 | #include "lxdream.h"
|
nkeynes@1023 | 21 | #include "gettext.h"
|
nkeynes@1023 | 22 | #include "gdrom/gddriver.h"
|
nkeynes@1023 | 23 | #include "gdrom/packet.h"
|
nkeynes@1023 | 24 |
|
nkeynes@1023 | 25 | #define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
|
nkeynes@1023 | 26 | #define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
|
nkeynes@1023 | 27 | #define MAX_SECTORS_PER_CALL 1
|
nkeynes@1023 | 28 |
|
nkeynes@1023 | 29 | #define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES_PER_SECOND) + (m*CD_FRAMES_PER_MINUTE))
|
nkeynes@1023 | 30 |
|
nkeynes@1023 | 31 | static void mmc_make_read_cd_cmd( char *cmd, uint32_t real_sector, int mode )
|
nkeynes@1023 | 32 | {
|
nkeynes@1023 | 33 | cmd[0] = 0xBE;
|
nkeynes@1023 | 34 | cmd[1] = (mode & 0x0E) << 1;
|
nkeynes@1023 | 35 | cmd[2] = (real_sector >> 24) & 0xFF;
|
nkeynes@1023 | 36 | cmd[3] = (real_sector >> 16) & 0xFF;
|
nkeynes@1023 | 37 | cmd[4] = (real_sector >> 8) & 0xFF;
|
nkeynes@1023 | 38 | cmd[5] = real_sector & 0xFF;
|
nkeynes@1023 | 39 | cmd[6] = 0;
|
nkeynes@1023 | 40 | cmd[7] = 0;
|
nkeynes@1023 | 41 | cmd[8] = 1;
|
nkeynes@1023 | 42 | cmd[9] = 0;
|
nkeynes@1023 | 43 | cmd[10]= 0;
|
nkeynes@1023 | 44 | cmd[11]= 0;
|
nkeynes@1023 | 45 |
|
nkeynes@1023 | 46 | if( READ_CD_RAW(mode) ) {
|
nkeynes@1023 | 47 | cmd[9] = 0xF0;
|
nkeynes@1023 | 48 | } else {
|
nkeynes@1023 | 49 | if( READ_CD_HEADER(mode) ) {
|
nkeynes@1023 | 50 | cmd[9] = 0xA0;
|
nkeynes@1023 | 51 | }
|
nkeynes@1023 | 52 | if( READ_CD_SUBHEAD(mode) ) {
|
nkeynes@1023 | 53 | cmd[9] |= 0x40;
|
nkeynes@1023 | 54 | }
|
nkeynes@1023 | 55 | if( READ_CD_DATA(mode) ) {
|
nkeynes@1023 | 56 | cmd[9] |= 0x10;
|
nkeynes@1023 | 57 | }
|
nkeynes@1023 | 58 | }
|
nkeynes@1023 | 59 | }
|
nkeynes@1023 | 60 |
|
nkeynes@1023 | 61 | /**
|
nkeynes@1023 | 62 | * Parse the TOC (format 2) into the gdrom_disc structure
|
nkeynes@1023 | 63 | */
|
nkeynes@1023 | 64 | void mmc_parse_toc2( gdrom_disc_t disc, unsigned char *buf )
|
nkeynes@1023 | 65 | {
|
nkeynes@1023 | 66 | int max_track = 0;
|
nkeynes@1023 | 67 | int last_track = -1;
|
nkeynes@1023 | 68 | int leadout = -1;
|
nkeynes@1023 | 69 | int len = (buf[0] << 8) | buf[1];
|
nkeynes@1023 | 70 | int session_type = -1;
|
nkeynes@1023 | 71 | int i;
|
nkeynes@1023 | 72 | for( i = 4; i<len; i+=11 ) {
|
nkeynes@1023 | 73 | int session = buf[i];
|
nkeynes@1023 | 74 | int adr = buf[i+1] >> 4;
|
nkeynes@1023 | 75 | int point = buf[i+3];
|
nkeynes@1023 | 76 | if( adr == 0x01 && point > 0 && point < 100 ) {
|
nkeynes@1023 | 77 | /* Track info */
|
nkeynes@1023 | 78 | int trackno = point-1;
|
nkeynes@1023 | 79 | if( point > max_track ) {
|
nkeynes@1023 | 80 | max_track = point;
|
nkeynes@1023 | 81 | }
|
nkeynes@1023 | 82 | disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
|
nkeynes@1023 | 83 | disc->track[trackno].session = session - 1;
|
nkeynes@1023 | 84 | disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
|
nkeynes@1023 | 85 | if( disc->track[trackno].flags & TRACK_DATA ) {
|
nkeynes@1023 | 86 | disc->track[trackno].mode = GDROM_MODE1;
|
nkeynes@1023 | 87 | } else {
|
nkeynes@1023 | 88 | disc->track[trackno].mode = GDROM_CDDA;
|
nkeynes@1023 | 89 | }
|
nkeynes@1023 | 90 | if( last_track != -1 ) {
|
nkeynes@1023 | 91 | disc->track[last_track].sector_count = disc->track[trackno].lba -
|
nkeynes@1023 | 92 | disc->track[last_track].lba;
|
nkeynes@1023 | 93 | }
|
nkeynes@1023 | 94 | last_track = trackno;
|
nkeynes@1023 | 95 | } else switch( (adr << 8) | point ) {
|
nkeynes@1023 | 96 | case 0x1A0: /* session info */
|
nkeynes@1023 | 97 | if( buf[i+9] == 0x20 ) {
|
nkeynes@1023 | 98 | session_type = IDE_DISC_CDROMXA;
|
nkeynes@1023 | 99 | } else {
|
nkeynes@1023 | 100 | session_type = IDE_DISC_CDROM;
|
nkeynes@1023 | 101 | }
|
nkeynes@1023 | 102 | disc->disc_type = session_type;
|
nkeynes@1023 | 103 | break;
|
nkeynes@1023 | 104 | case 0x1A2: /* leadout */
|
nkeynes@1023 | 105 | leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
|
nkeynes@1023 | 106 | break;
|
nkeynes@1023 | 107 | }
|
nkeynes@1023 | 108 | }
|
nkeynes@1023 | 109 | disc->track_count = max_track;
|
nkeynes@1023 | 110 |
|
nkeynes@1023 | 111 | if( leadout != -1 && last_track != -1 ) {
|
nkeynes@1023 | 112 | disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
|
nkeynes@1023 | 113 | }
|
nkeynes@1023 | 114 | }
|
nkeynes@1023 | 115 |
|
nkeynes@1023 | 116 |
|
nkeynes@1023 | 117 | /**
|
nkeynes@1023 | 118 | * Construct a drive indentification string based on the response to the
|
nkeynes@1023 | 119 | * INQUIRY command. On success, the disc display_name is updated with the
|
nkeynes@1023 | 120 | * drive name, otherwise the display_name is unchanged.
|
nkeynes@1023 | 121 | * @return PKT_ERR_OK on success, otherwise the host failure code.
|
nkeynes@1023 | 122 | */
|
nkeynes@1023 | 123 | static gdrom_error_t gdrom_scsi_identify_drive( gdrom_disc_t disc )
|
nkeynes@1023 | 124 | {
|
nkeynes@1023 | 125 | unsigned char ident[256];
|
nkeynes@1023 | 126 | uint32_t identlen = 256;
|
nkeynes@1023 | 127 | char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0};
|
nkeynes@1023 | 128 | gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, ident, &identlen );
|
nkeynes@1023 | 129 | if( status == PKT_ERR_OK ) {
|
nkeynes@1023 | 130 | char vendorid[9];
|
nkeynes@1023 | 131 | char productid[17];
|
nkeynes@1023 | 132 | char productrev[5];
|
nkeynes@1023 | 133 | memcpy( vendorid, ident+8, 8 ); vendorid[8] = 0;
|
nkeynes@1023 | 134 | memcpy( productid, ident+16, 16 ); productid[16] = 0;
|
nkeynes@1023 | 135 | memcpy( productrev, ident+32, 4 ); productrev[4] = 0;
|
nkeynes@1023 | 136 | g_free( (char *)disc->display_name );
|
nkeynes@1023 | 137 | disc->display_name = g_strdup_printf( "%.8s %.16s %.4s", g_strstrip(vendorid),
|
nkeynes@1023 | 138 | g_strstrip(productid), g_strstrip(productrev) );
|
nkeynes@1023 | 139 | }
|
nkeynes@1023 | 140 | return status;
|
nkeynes@1023 | 141 | }
|
nkeynes@1023 | 142 |
|
nkeynes@1023 | 143 |
|
nkeynes@1023 | 144 | static gdrom_error_t gdrom_scsi_read_sector( gdrom_disc_t disc, uint32_t sector,
|
nkeynes@1023 | 145 | int mode, unsigned char *buf, uint32_t *length )
|
nkeynes@1023 | 146 | {
|
nkeynes@1023 | 147 | uint32_t real_sector = sector - GDROM_PREGAP;
|
nkeynes@1023 | 148 | uint32_t sector_size = MAX_SECTOR_SIZE;
|
nkeynes@1023 | 149 | char cmd[12];
|
nkeynes@1023 | 150 |
|
nkeynes@1023 | 151 | mmc_make_read_cd_cmd( cmd, real_sector, mode );
|
nkeynes@1023 | 152 |
|
nkeynes@1023 | 153 | gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, buf, §or_size );
|
nkeynes@1023 | 154 | if( status != 0 ) {
|
nkeynes@1023 | 155 | return status;
|
nkeynes@1023 | 156 | }
|
nkeynes@1023 | 157 | /* FIXME */
|
nkeynes@1023 | 158 | *length = 2048;
|
nkeynes@1023 | 159 | return 0;
|
nkeynes@1023 | 160 | }
|
nkeynes@1023 | 161 |
|
nkeynes@1023 | 162 | /**
|
nkeynes@1023 | 163 | * Read the full table of contents into the disc from the device.
|
nkeynes@1023 | 164 | */
|
nkeynes@1023 | 165 | static gdrom_error_t gdrom_scsi_read_toc( gdrom_disc_t disc )
|
nkeynes@1023 | 166 | {
|
nkeynes@1023 | 167 | unsigned char buf[MAXTOCSIZE];
|
nkeynes@1023 | 168 | uint32_t buflen = sizeof(buf);
|
nkeynes@1023 | 169 | char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
nkeynes@1023 | 170 |
|
nkeynes@1023 | 171 | cmd[7] = (sizeof(buf))>>8;
|
nkeynes@1023 | 172 | cmd[8] = (sizeof(buf))&0xFF;
|
nkeynes@1023 | 173 | memset( buf, 0, sizeof(buf) );
|
nkeynes@1023 | 174 | gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read(disc, cmd, buf, &buflen );
|
nkeynes@1023 | 175 | if( status == PKT_ERR_OK ) {
|
nkeynes@1023 | 176 | mmc_parse_toc2( disc, buf );
|
nkeynes@1023 | 177 | } else {
|
nkeynes@1023 | 178 | if( status & 0xFF != 0x02 ) {
|
nkeynes@1023 | 179 | /* Sense key 2 == Not Ready (ie temporary failure). Just ignore and
|
nkeynes@1023 | 180 | * consider the drive empty for now, but warn about any other errors
|
nkeynes@1023 | 181 | * we get. */
|
nkeynes@1023 | 182 | WARN( _("Unable to read disc table of contents (error %04x)"), status );
|
nkeynes@1023 | 183 | }
|
nkeynes@1023 | 184 | disc->disc_type = IDE_DISC_NONE;
|
nkeynes@1023 | 185 | }
|
nkeynes@1023 | 186 | return status;
|
nkeynes@1023 | 187 | }
|
nkeynes@1023 | 188 |
|
nkeynes@1023 | 189 | static gboolean gdrom_scsi_check_status( gdrom_disc_t disc )
|
nkeynes@1023 | 190 | {
|
nkeynes@1023 | 191 | if( SCSI_TRANSPORT(disc)->media_changed(disc) ) {
|
nkeynes@1023 | 192 | gdrom_scsi_read_toc(disc);
|
nkeynes@1023 | 193 | return TRUE;
|
nkeynes@1023 | 194 | } else {
|
nkeynes@1023 | 195 | return FALSE;
|
nkeynes@1023 | 196 | }
|
nkeynes@1023 | 197 | }
|
nkeynes@1023 | 198 |
|
nkeynes@1023 | 199 | static gdrom_error_t gdrom_scsi_play_audio( gdrom_disc_t disc, uint32_t lba, uint32_t endlba )
|
nkeynes@1023 | 200 | {
|
nkeynes@1023 | 201 | uint32_t real_sector = lba - GDROM_PREGAP;
|
nkeynes@1023 | 202 | uint32_t length = endlba - lba;
|
nkeynes@1023 | 203 |
|
nkeynes@1023 | 204 | char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
|
nkeynes@1023 | 205 | cmd[2] = (real_sector >> 24) & 0xFF;
|
nkeynes@1023 | 206 | cmd[3] = (real_sector >> 16) & 0xFF;
|
nkeynes@1023 | 207 | cmd[4] = (real_sector >> 8) & 0xFF;
|
nkeynes@1023 | 208 | cmd[5] = real_sector & 0xFF;
|
nkeynes@1023 | 209 | cmd[6] = (length >> 24) & 0xFF;
|
nkeynes@1023 | 210 | cmd[7] = (length >> 16) & 0xFF;
|
nkeynes@1023 | 211 | cmd[8] = (length >> 8) & 0xFF;
|
nkeynes@1023 | 212 | cmd[9] = length & 0xFF;
|
nkeynes@1023 | 213 |
|
nkeynes@1023 | 214 | return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
|
nkeynes@1023 | 215 | }
|
nkeynes@1023 | 216 |
|
nkeynes@1023 | 217 |
|
nkeynes@1023 | 218 | gdrom_error_t gdrom_scsi_stop_audio( gdrom_disc_t disc )
|
nkeynes@1023 | 219 | {
|
nkeynes@1023 | 220 | int fd = fileno(disc->file);
|
nkeynes@1023 | 221 | uint32_t buflen = 0;
|
nkeynes@1023 | 222 | char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
|
nkeynes@1023 | 223 |
|
nkeynes@1023 | 224 | return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
|
nkeynes@1023 | 225 | }
|
nkeynes@1023 | 226 |
|
nkeynes@1023 | 227 |
|
nkeynes@1023 | 228 | gdrom_disc_t gdrom_scsi_disc_new( const gchar *filename, FILE *f, gdrom_scsi_transport_t scsi )
|
nkeynes@1023 | 229 | {
|
nkeynes@1023 | 230 | gdrom_disc_t disc = gdrom_disc_new(filename,f);
|
nkeynes@1023 | 231 | if( disc != NULL ) {
|
nkeynes@1023 | 232 | /* Initialize */
|
nkeynes@1023 | 233 | disc->impl_data = scsi;
|
nkeynes@1023 | 234 | disc->check_status = gdrom_scsi_check_status;
|
nkeynes@1023 | 235 | disc->read_sector = gdrom_scsi_read_sector;
|
nkeynes@1023 | 236 | disc->play_audio = gdrom_scsi_play_audio;
|
nkeynes@1023 | 237 | disc->run_time_slice = NULL;
|
nkeynes@1023 | 238 | gdrom_scsi_identify_drive(disc);
|
nkeynes@1023 | 239 | gdrom_scsi_read_toc(disc);
|
nkeynes@1023 | 240 | }
|
nkeynes@1023 | 241 | return disc;
|
nkeynes@1023 | 242 | } |