nkeynes@1023: /** nkeynes@1023: * $Id$ nkeynes@1023: * nkeynes@1023: * SCSI/MMC device interface (depends on lower-level SCSI transport) nkeynes@1023: * nkeynes@1023: * Copyright (c) 2009 Nathan Keynes. nkeynes@1023: * nkeynes@1023: * This program is free software; you can redistribute it and/or modify nkeynes@1023: * it under the terms of the GNU General Public License as published by nkeynes@1023: * the Free Software Foundation; either version 2 of the License, or nkeynes@1023: * (at your option) any later version. nkeynes@1023: * nkeynes@1023: * This program is distributed in the hope that it will be useful, nkeynes@1023: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@1023: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@1023: * GNU General Public License for more details. nkeynes@1023: */ nkeynes@1023: nkeynes@1097: #include nkeynes@1023: #include nkeynes@1296: #include nkeynes@1023: #include "lxdream.h" nkeynes@1023: #include "gettext.h" nkeynes@1097: #include "drivers/cdrom/cdimpl.h" nkeynes@1023: nkeynes@1023: #define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */ nkeynes@1023: #define MAXTOCSIZE 4 + (MAXTOCENTRIES*11) nkeynes@1023: #define MAX_SECTORS_PER_CALL 1 nkeynes@1023: nkeynes@1023: /** nkeynes@1097: * Parse the TOC (format 2) into the cdrom_disc structure nkeynes@1023: */ nkeynes@1097: void mmc_parse_toc2( cdrom_disc_t disc, unsigned char *buf ) nkeynes@1023: { nkeynes@1023: int max_track = 0; nkeynes@1097: int max_session = 0; nkeynes@1023: int last_track = -1; nkeynes@1023: int leadout = -1; nkeynes@1023: int len = (buf[0] << 8) | buf[1]; nkeynes@1023: int session_type = -1; nkeynes@1023: int i; nkeynes@1023: for( i = 4; i> 4; nkeynes@1023: int point = buf[i+3]; nkeynes@1023: if( adr == 0x01 && point > 0 && point < 100 ) { nkeynes@1023: /* Track info */ nkeynes@1023: int trackno = point-1; nkeynes@1023: if( point > max_track ) { nkeynes@1023: max_track = point; nkeynes@1023: } nkeynes@1097: if( session > max_session ) { nkeynes@1097: max_session = session; nkeynes@1097: } nkeynes@1097: disc->track[trackno].trackno = point; nkeynes@1023: disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4; nkeynes@1097: disc->track[trackno].sessionno = session; nkeynes@1023: disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]); nkeynes@1023: last_track = trackno; nkeynes@1023: } else switch( (adr << 8) | point ) { nkeynes@1023: case 0x1A0: /* session info */ nkeynes@1023: if( buf[i+9] == 0x20 ) { nkeynes@1097: session_type = CDROM_DISC_XA; nkeynes@1023: } else { nkeynes@1097: session_type = CDROM_DISC_NONXA; nkeynes@1023: } nkeynes@1023: disc->disc_type = session_type; nkeynes@1023: break; nkeynes@1023: case 0x1A2: /* leadout */ nkeynes@1097: disc->leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]); nkeynes@1023: break; nkeynes@1023: } nkeynes@1023: } nkeynes@1023: disc->track_count = max_track; nkeynes@1097: disc->session_count = max_session; nkeynes@1023: } nkeynes@1023: nkeynes@1023: nkeynes@1097: const char *mmc_parse_inquiry( unsigned char *buf ) nkeynes@1097: { nkeynes@1097: char vendorid[9]; nkeynes@1097: char productid[17]; nkeynes@1097: char productrev[5]; nkeynes@1097: memcpy( vendorid, buf+8, 8 ); vendorid[8] = 0; nkeynes@1097: memcpy( productid, buf+16, 16 ); productid[16] = 0; nkeynes@1097: memcpy( productrev, buf+32, 4 ); productrev[4] = 0; nkeynes@1097: return g_strdup_printf( "%.8s %.16s %.4s", g_strstrip(vendorid), nkeynes@1097: g_strstrip(productid), g_strstrip(productrev) ); nkeynes@1097: } nkeynes@1097: nkeynes@1023: /** nkeynes@1023: * Construct a drive indentification string based on the response to the nkeynes@1097: * INQUIRY command. On success, returns the disc identification as a newly nkeynes@1097: * allocated string, otherwise NULL. nkeynes@1023: */ nkeynes@1097: const char *cdrom_disc_scsi_identify_drive( cdrom_disc_t disc ) nkeynes@1023: { nkeynes@1023: unsigned char ident[256]; nkeynes@1023: uint32_t identlen = 256; nkeynes@1023: char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0}; nkeynes@1097: cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, ident, &identlen ); nkeynes@1097: if( status == CDROM_ERROR_OK ) { nkeynes@1097: return mmc_parse_inquiry(ident); nkeynes@1023: } nkeynes@1097: return NULL; nkeynes@1023: } nkeynes@1023: nkeynes@1023: nkeynes@1097: static cdrom_error_t cdrom_disc_scsi_read_sectors( sector_source_t source, cdrom_lba_t lba, nkeynes@1097: cdrom_count_t count, cdrom_read_mode_t mode, unsigned char *buf, size_t *length ) nkeynes@1023: { nkeynes@1097: assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) ); nkeynes@1097: cdrom_disc_t disc = (cdrom_disc_t)source; nkeynes@1097: uint32_t sector_size = CDROM_MAX_SECTOR_SIZE; nkeynes@1023: char cmd[12]; nkeynes@1023: nkeynes@1097: cmd[0] = 0xBE; nkeynes@1097: cmd[1] = CDROM_READ_TYPE(mode); nkeynes@1097: cmd[2] = (lba >> 24) & 0xFF; nkeynes@1097: cmd[3] = (lba >> 16) & 0xFF; nkeynes@1097: cmd[4] = (lba >> 8) & 0xFF; nkeynes@1097: cmd[5] = lba & 0xFF; nkeynes@1097: cmd[6] = (count>>16) & 0xFF; nkeynes@1097: cmd[7] = (count>>8) & 0xFF; nkeynes@1097: cmd[8] = count & 0xFF; nkeynes@1097: cmd[9] = CDROM_READ_FIELDS(mode)>>8; nkeynes@1097: cmd[10]= 0; nkeynes@1097: cmd[11]= 0; nkeynes@1023: nkeynes@1097: cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, buf, §or_size ); nkeynes@1023: if( status != 0 ) { nkeynes@1023: return status; nkeynes@1023: } nkeynes@1023: /* FIXME */ nkeynes@1023: *length = 2048; nkeynes@1023: return 0; nkeynes@1023: } nkeynes@1023: nkeynes@1023: /** nkeynes@1023: * Read the full table of contents into the disc from the device. nkeynes@1023: */ nkeynes@1097: gboolean cdrom_disc_scsi_read_toc( cdrom_disc_t disc, ERROR *err ) nkeynes@1023: { nkeynes@1023: unsigned char buf[MAXTOCSIZE]; nkeynes@1023: uint32_t buflen = sizeof(buf); nkeynes@1023: char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; nkeynes@1023: nkeynes@1023: cmd[7] = (sizeof(buf))>>8; nkeynes@1023: cmd[8] = (sizeof(buf))&0xFF; nkeynes@1023: memset( buf, 0, sizeof(buf) ); nkeynes@1097: cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read(disc, cmd, buf, &buflen ); nkeynes@1097: if( status == CDROM_ERROR_OK ) { nkeynes@1097: mmc_parse_toc2( disc, buf ); nkeynes@1097: return TRUE; nkeynes@1023: } else { nkeynes@1071: if( (status & 0xFF) != 0x02 ) { nkeynes@1023: /* Sense key 2 == Not Ready (ie temporary failure). Just ignore and nkeynes@1023: * consider the drive empty for now, but warn about any other errors nkeynes@1023: * we get. */ nkeynes@1023: WARN( _("Unable to read disc table of contents (error %04x)"), status ); nkeynes@1023: } nkeynes@1097: cdrom_disc_clear_toc(disc); nkeynes@1097: return FALSE; nkeynes@1023: } nkeynes@1023: } nkeynes@1023: nkeynes@1097: static gboolean cdrom_disc_scsi_check_media( cdrom_disc_t disc ) nkeynes@1023: { nkeynes@1023: if( SCSI_TRANSPORT(disc)->media_changed(disc) ) { nkeynes@1097: cdrom_disc_scsi_read_toc(disc, NULL); nkeynes@1023: return TRUE; nkeynes@1023: } else { nkeynes@1023: return FALSE; nkeynes@1023: } nkeynes@1023: } nkeynes@1023: nkeynes@1097: static cdrom_error_t cdrom_disc_scsi_play_audio( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t length ) nkeynes@1023: { nkeynes@1023: char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 }; nkeynes@1097: cmd[2] = (lba >> 24) & 0xFF; nkeynes@1097: cmd[3] = (lba >> 16) & 0xFF; nkeynes@1097: cmd[4] = (lba >> 8) & 0xFF; nkeynes@1097: cmd[5] = lba & 0xFF; nkeynes@1023: cmd[6] = (length >> 24) & 0xFF; nkeynes@1023: cmd[7] = (length >> 16) & 0xFF; nkeynes@1023: cmd[8] = (length >> 8) & 0xFF; nkeynes@1023: cmd[9] = length & 0xFF; nkeynes@1023: nkeynes@1023: return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd ); nkeynes@1023: } nkeynes@1023: nkeynes@1023: nkeynes@1097: static cdrom_error_t cdrom_disc_scsi_stop_audio( cdrom_disc_t disc ) nkeynes@1023: { nkeynes@1023: uint32_t buflen = 0; nkeynes@1023: char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0}; nkeynes@1023: nkeynes@1023: return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd ); nkeynes@1023: } nkeynes@1023: nkeynes@1097: void cdrom_disc_scsi_init( cdrom_disc_t disc, cdrom_scsi_transport_t scsi ) nkeynes@1097: { nkeynes@1097: disc->impl_data = scsi; nkeynes@1097: disc->source.read_sectors = cdrom_disc_scsi_read_sectors; nkeynes@1097: disc->read_toc = cdrom_disc_scsi_read_toc; nkeynes@1097: disc->check_media = cdrom_disc_scsi_check_media; nkeynes@1097: disc->play_audio = cdrom_disc_scsi_play_audio; nkeynes@1097: disc->stop_audio = cdrom_disc_scsi_stop_audio; nkeynes@1097: } nkeynes@1023: nkeynes@1097: cdrom_disc_t cdrom_disc_scsi_new( const char *filename, cdrom_scsi_transport_t scsi, ERROR *err ) nkeynes@1023: { nkeynes@1097: cdrom_disc_t disc = cdrom_disc_new(filename, err); nkeynes@1023: if( disc != NULL ) { nkeynes@1097: /* Initialize */ nkeynes@1097: cdrom_disc_scsi_init( disc, scsi ); nkeynes@1097: cdrom_disc_read_toc(disc, err); nkeynes@1023: } nkeynes@1023: return disc; nkeynes@1071: } nkeynes@1097: nkeynes@1097: cdrom_disc_t cdrom_disc_scsi_new_file( FILE *f, const char *filename, cdrom_scsi_transport_t scsi, ERROR *err ) nkeynes@1097: { nkeynes@1097: cdrom_disc_t disc = cdrom_disc_new(filename, err); nkeynes@1097: if( disc != NULL ) { nkeynes@1097: /* Initialize */ nkeynes@1097: disc->base_source = file_sector_source_new( f, SECTOR_UNKNOWN, 0, 0, TRUE ); nkeynes@1097: if( disc->base_source != NULL ) { nkeynes@1097: sector_source_ref( disc->base_source ); nkeynes@1097: cdrom_disc_scsi_init( disc, scsi ); nkeynes@1097: cdrom_disc_read_toc(disc, err); nkeynes@1097: } else { nkeynes@1097: cdrom_disc_unref(disc); nkeynes@1097: disc = NULL; nkeynes@1097: } nkeynes@1097: } nkeynes@1097: return disc; nkeynes@1097: }