Search
lxdream.org :: lxdream/src/drivers/cd_mmc.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cd_mmc.c
changeset 1023:264e2fd90be8
next1071:182cfe43c09e
author nkeynes
date Sat Jun 13 00:50:48 2009 +0000 (14 years ago)
permissions -rw-r--r--
last change Build drivers with library dependencies as shared objects (ie plugins)
file annotate diff log raw
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, &sector_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
} 
.