4 * SCSI/MMC device interface (depends on lower-level SCSI transport)
6 * Copyright (c) 2009 Nathan Keynes.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
22 #include "gdrom/gddriver.h"
23 #include "gdrom/packet.h"
25 #define MAXTOCENTRIES 600 /* This is a fairly generous overestimate really */
26 #define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
27 #define MAX_SECTORS_PER_CALL 1
29 #define MSFTOLBA( m,s,f ) (f + (s*CD_FRAMES_PER_SECOND) + (m*CD_FRAMES_PER_MINUTE))
31 static void mmc_make_read_cd_cmd( char *cmd, uint32_t real_sector, int mode )
34 cmd[1] = (mode & 0x0E) << 1;
35 cmd[2] = (real_sector >> 24) & 0xFF;
36 cmd[3] = (real_sector >> 16) & 0xFF;
37 cmd[4] = (real_sector >> 8) & 0xFF;
38 cmd[5] = real_sector & 0xFF;
46 if( READ_CD_RAW(mode) ) {
49 if( READ_CD_HEADER(mode) ) {
52 if( READ_CD_SUBHEAD(mode) ) {
55 if( READ_CD_DATA(mode) ) {
62 * Parse the TOC (format 2) into the gdrom_disc structure
64 void mmc_parse_toc2( gdrom_disc_t disc, unsigned char *buf )
69 int len = (buf[0] << 8) | buf[1];
70 int session_type = -1;
72 for( i = 4; i<len; i+=11 ) {
74 int adr = buf[i+1] >> 4;
76 if( adr == 0x01 && point > 0 && point < 100 ) {
78 int trackno = point-1;
79 if( point > max_track ) {
82 disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
83 disc->track[trackno].session = session - 1;
84 disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
85 if( disc->track[trackno].flags & TRACK_DATA ) {
86 disc->track[trackno].mode = GDROM_MODE1;
88 disc->track[trackno].mode = GDROM_CDDA;
90 if( last_track != -1 ) {
91 disc->track[last_track].sector_count = disc->track[trackno].lba -
92 disc->track[last_track].lba;
95 } else switch( (adr << 8) | point ) {
96 case 0x1A0: /* session info */
97 if( buf[i+9] == 0x20 ) {
98 session_type = IDE_DISC_CDROMXA;
100 session_type = IDE_DISC_CDROM;
102 disc->disc_type = session_type;
104 case 0x1A2: /* leadout */
105 leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
109 disc->track_count = max_track;
111 if( leadout != -1 && last_track != -1 ) {
112 disc->track[last_track].sector_count = leadout - disc->track[last_track].lba;
118 * Construct a drive indentification string based on the response to the
119 * INQUIRY command. On success, the disc display_name is updated with the
120 * drive name, otherwise the display_name is unchanged.
121 * @return PKT_ERR_OK on success, otherwise the host failure code.
123 static gdrom_error_t gdrom_scsi_identify_drive( gdrom_disc_t disc )
125 unsigned char ident[256];
126 uint32_t identlen = 256;
127 char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0};
128 gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, ident, &identlen );
129 if( status == PKT_ERR_OK ) {
133 memcpy( vendorid, ident+8, 8 ); vendorid[8] = 0;
134 memcpy( productid, ident+16, 16 ); productid[16] = 0;
135 memcpy( productrev, ident+32, 4 ); productrev[4] = 0;
136 g_free( (char *)disc->display_name );
137 disc->display_name = g_strdup_printf( "%.8s %.16s %.4s", g_strstrip(vendorid),
138 g_strstrip(productid), g_strstrip(productrev) );
144 static gdrom_error_t gdrom_scsi_read_sector( gdrom_disc_t disc, uint32_t sector,
145 int mode, unsigned char *buf, uint32_t *length )
147 uint32_t real_sector = sector - GDROM_PREGAP;
148 uint32_t sector_size = MAX_SECTOR_SIZE;
151 mmc_make_read_cd_cmd( cmd, real_sector, mode );
153 gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, buf, §or_size );
163 * Read the full table of contents into the disc from the device.
165 static gdrom_error_t gdrom_scsi_read_toc( gdrom_disc_t disc )
167 unsigned char buf[MAXTOCSIZE];
168 uint32_t buflen = sizeof(buf);
169 char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
171 cmd[7] = (sizeof(buf))>>8;
172 cmd[8] = (sizeof(buf))&0xFF;
173 memset( buf, 0, sizeof(buf) );
174 gdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read(disc, cmd, buf, &buflen );
175 if( status == PKT_ERR_OK ) {
176 mmc_parse_toc2( disc, buf );
178 if( status & 0xFF != 0x02 ) {
179 /* Sense key 2 == Not Ready (ie temporary failure). Just ignore and
180 * consider the drive empty for now, but warn about any other errors
182 WARN( _("Unable to read disc table of contents (error %04x)"), status );
184 disc->disc_type = IDE_DISC_NONE;
189 static gboolean gdrom_scsi_check_status( gdrom_disc_t disc )
191 if( SCSI_TRANSPORT(disc)->media_changed(disc) ) {
192 gdrom_scsi_read_toc(disc);
199 static gdrom_error_t gdrom_scsi_play_audio( gdrom_disc_t disc, uint32_t lba, uint32_t endlba )
201 uint32_t real_sector = lba - GDROM_PREGAP;
202 uint32_t length = endlba - lba;
204 char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
205 cmd[2] = (real_sector >> 24) & 0xFF;
206 cmd[3] = (real_sector >> 16) & 0xFF;
207 cmd[4] = (real_sector >> 8) & 0xFF;
208 cmd[5] = real_sector & 0xFF;
209 cmd[6] = (length >> 24) & 0xFF;
210 cmd[7] = (length >> 16) & 0xFF;
211 cmd[8] = (length >> 8) & 0xFF;
212 cmd[9] = length & 0xFF;
214 return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
218 gdrom_error_t gdrom_scsi_stop_audio( gdrom_disc_t disc )
220 int fd = fileno(disc->file);
222 char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
224 return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
228 gdrom_disc_t gdrom_scsi_disc_new( const gchar *filename, FILE *f, gdrom_scsi_transport_t scsi )
230 gdrom_disc_t disc = gdrom_disc_new(filename,f);
233 disc->impl_data = scsi;
234 disc->check_status = gdrom_scsi_check_status;
235 disc->read_sector = gdrom_scsi_read_sector;
236 disc->play_audio = gdrom_scsi_play_audio;
237 disc->run_time_slice = NULL;
238 gdrom_scsi_identify_drive(disc);
239 gdrom_scsi_read_toc(disc);
.