Search
lxdream.org :: lxdream/src/drivers/cdrom/cd_mmc.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/drivers/cdrom/cd_mmc.c
changeset 1097:d4807997e450
prevsrc/drivers/cd_mmc.c@1071:182cfe43c09e
next1296:30ecee61f811
author nkeynes
date Sun Jan 31 18:35:06 2010 +1000 (14 years ago)
permissions -rw-r--r--
last change Refactor CDROM host support
- Completely separate GDROM hardware (in gdrom/gdrom.c) from generic CDROM
support (now in drivers/cdrom)
- Add concept of 'sector sources' that can be mixed and matched to create
cdrom discs (makes support of arbitrary disc types much simpler)
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@1097
    19
#include <assert.h>
nkeynes@1023
    20
#include <string.h>
nkeynes@1097
    21
#include <glib/gstrfuncs.h>
nkeynes@1023
    22
#include "lxdream.h"
nkeynes@1023
    23
#include "gettext.h"
nkeynes@1097
    24
#include "drivers/cdrom/cdimpl.h"
nkeynes@1023
    25
nkeynes@1023
    26
#define MAXTOCENTRIES 600  /* This is a fairly generous overestimate really */
nkeynes@1023
    27
#define MAXTOCSIZE 4 + (MAXTOCENTRIES*11)
nkeynes@1023
    28
#define MAX_SECTORS_PER_CALL 1
nkeynes@1023
    29
nkeynes@1023
    30
/**
nkeynes@1097
    31
 * Parse the TOC (format 2) into the cdrom_disc structure
nkeynes@1023
    32
 */
nkeynes@1097
    33
void mmc_parse_toc2( cdrom_disc_t disc, unsigned char *buf )
nkeynes@1023
    34
{
nkeynes@1023
    35
    int max_track = 0;
nkeynes@1097
    36
    int max_session = 0;
nkeynes@1023
    37
    int last_track = -1;
nkeynes@1023
    38
    int leadout = -1;
nkeynes@1023
    39
    int len = (buf[0] << 8) | buf[1];
nkeynes@1023
    40
    int session_type = -1;
nkeynes@1023
    41
    int i;
nkeynes@1023
    42
    for( i = 4; i<len; i+=11 ) {
nkeynes@1023
    43
        int session = buf[i];
nkeynes@1023
    44
        int adr = buf[i+1] >> 4;
nkeynes@1023
    45
        int point = buf[i+3];
nkeynes@1023
    46
        if( adr == 0x01 && point > 0 && point < 100 ) {
nkeynes@1023
    47
            /* Track info */
nkeynes@1023
    48
            int trackno = point-1;
nkeynes@1023
    49
            if( point > max_track ) {
nkeynes@1023
    50
                max_track = point;
nkeynes@1023
    51
            }
nkeynes@1097
    52
            if( session > max_session ) {
nkeynes@1097
    53
                max_session = session;
nkeynes@1097
    54
            }
nkeynes@1097
    55
            disc->track[trackno].trackno = point;
nkeynes@1023
    56
            disc->track[trackno].flags = (buf[i+1] & 0x0F) << 4;
nkeynes@1097
    57
            disc->track[trackno].sessionno = session;
nkeynes@1023
    58
            disc->track[trackno].lba = MSFTOLBA(buf[i+8],buf[i+9],buf[i+10]);
nkeynes@1023
    59
            last_track = trackno;
nkeynes@1023
    60
        } else switch( (adr << 8) | point ) {
nkeynes@1023
    61
        case 0x1A0: /* session info */
nkeynes@1023
    62
            if( buf[i+9] == 0x20 ) {
nkeynes@1097
    63
                session_type = CDROM_DISC_XA;
nkeynes@1023
    64
            } else {
nkeynes@1097
    65
                session_type = CDROM_DISC_NONXA;
nkeynes@1023
    66
            }
nkeynes@1023
    67
            disc->disc_type = session_type;
nkeynes@1023
    68
            break;
nkeynes@1023
    69
        case 0x1A2: /* leadout */
nkeynes@1097
    70
            disc->leadout = MSFTOLBA(buf[i+8], buf[i+9], buf[i+10]);
nkeynes@1023
    71
            break;
nkeynes@1023
    72
        }
nkeynes@1023
    73
    }
nkeynes@1023
    74
    disc->track_count = max_track;
nkeynes@1097
    75
    disc->session_count = max_session;
nkeynes@1023
    76
}
nkeynes@1023
    77
nkeynes@1023
    78
nkeynes@1097
    79
const char *mmc_parse_inquiry( unsigned char *buf )
nkeynes@1097
    80
{
nkeynes@1097
    81
    char vendorid[9];
nkeynes@1097
    82
    char productid[17];
nkeynes@1097
    83
    char productrev[5];
nkeynes@1097
    84
    memcpy( vendorid, buf+8, 8 ); vendorid[8] = 0;
nkeynes@1097
    85
    memcpy( productid, buf+16, 16 ); productid[16] = 0;
nkeynes@1097
    86
    memcpy( productrev, buf+32, 4 ); productrev[4] = 0;
nkeynes@1097
    87
    return g_strdup_printf( "%.8s %.16s %.4s", g_strstrip(vendorid),
nkeynes@1097
    88
              g_strstrip(productid), g_strstrip(productrev) );
nkeynes@1097
    89
}
nkeynes@1097
    90
nkeynes@1023
    91
/**
nkeynes@1023
    92
 * Construct a drive indentification string based on the response to the
nkeynes@1097
    93
 * INQUIRY command. On success, returns the disc identification as a newly
nkeynes@1097
    94
 * allocated string, otherwise NULL.
nkeynes@1023
    95
 */
nkeynes@1097
    96
const char *cdrom_disc_scsi_identify_drive( cdrom_disc_t disc )
nkeynes@1023
    97
{
nkeynes@1023
    98
    unsigned char ident[256];
nkeynes@1023
    99
    uint32_t identlen = 256;
nkeynes@1023
   100
    char cmd[12] = {0x12,0,0,0, 0xFF,0,0,0, 0,0,0,0};
nkeynes@1097
   101
    cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, ident, &identlen );
nkeynes@1097
   102
    if( status == CDROM_ERROR_OK ) {
nkeynes@1097
   103
        return mmc_parse_inquiry(ident);
nkeynes@1023
   104
    }
nkeynes@1097
   105
    return NULL;
nkeynes@1023
   106
}
nkeynes@1023
   107
nkeynes@1023
   108
nkeynes@1097
   109
static cdrom_error_t cdrom_disc_scsi_read_sectors( sector_source_t source, cdrom_lba_t lba,
nkeynes@1097
   110
        cdrom_count_t count, cdrom_read_mode_t mode, unsigned char *buf, size_t *length )
nkeynes@1023
   111
{
nkeynes@1097
   112
    assert( IS_SECTOR_SOURCE_TYPE(source,DISC_SECTOR_SOURCE) );
nkeynes@1097
   113
    cdrom_disc_t disc = (cdrom_disc_t)source;
nkeynes@1097
   114
    uint32_t sector_size = CDROM_MAX_SECTOR_SIZE;
nkeynes@1023
   115
    char cmd[12];
nkeynes@1023
   116
nkeynes@1097
   117
    cmd[0] = 0xBE;
nkeynes@1097
   118
    cmd[1] = CDROM_READ_TYPE(mode);
nkeynes@1097
   119
    cmd[2] = (lba >> 24) & 0xFF;
nkeynes@1097
   120
    cmd[3] = (lba >> 16) & 0xFF;
nkeynes@1097
   121
    cmd[4] = (lba >> 8) & 0xFF;
nkeynes@1097
   122
    cmd[5] = lba & 0xFF;
nkeynes@1097
   123
    cmd[6] = (count>>16) & 0xFF;
nkeynes@1097
   124
    cmd[7] = (count>>8) & 0xFF;
nkeynes@1097
   125
    cmd[8] = count & 0xFF;
nkeynes@1097
   126
    cmd[9] = CDROM_READ_FIELDS(mode)>>8;
nkeynes@1097
   127
    cmd[10]= 0;
nkeynes@1097
   128
    cmd[11]= 0;
nkeynes@1023
   129
nkeynes@1097
   130
    cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read( disc, cmd, buf, &sector_size );
nkeynes@1023
   131
    if( status != 0 ) {
nkeynes@1023
   132
        return status;
nkeynes@1023
   133
    }
nkeynes@1023
   134
    /* FIXME */
nkeynes@1023
   135
    *length = 2048;
nkeynes@1023
   136
    return 0;
nkeynes@1023
   137
}
nkeynes@1023
   138
nkeynes@1023
   139
/**
nkeynes@1023
   140
 * Read the full table of contents into the disc from the device.
nkeynes@1023
   141
 */
nkeynes@1097
   142
gboolean cdrom_disc_scsi_read_toc( cdrom_disc_t disc, ERROR *err )
nkeynes@1023
   143
{
nkeynes@1023
   144
    unsigned char buf[MAXTOCSIZE];
nkeynes@1023
   145
    uint32_t buflen = sizeof(buf);
nkeynes@1023
   146
    char cmd[12] = { 0x43, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
nkeynes@1023
   147
nkeynes@1023
   148
    cmd[7] = (sizeof(buf))>>8;
nkeynes@1023
   149
    cmd[8] = (sizeof(buf))&0xFF;
nkeynes@1023
   150
    memset( buf, 0, sizeof(buf) );
nkeynes@1097
   151
    cdrom_error_t status = SCSI_TRANSPORT(disc)->packet_read(disc, cmd, buf, &buflen );
nkeynes@1097
   152
    if( status == CDROM_ERROR_OK ) {
nkeynes@1097
   153
	    mmc_parse_toc2( disc, buf );
nkeynes@1097
   154
	    return TRUE;
nkeynes@1023
   155
    } else {
nkeynes@1071
   156
    	if( (status & 0xFF) != 0x02 ) {
nkeynes@1023
   157
    	    /* Sense key 2 == Not Ready (ie temporary failure). Just ignore and
nkeynes@1023
   158
    	     * consider the drive empty for now, but warn about any other errors
nkeynes@1023
   159
    	     * we get. */
nkeynes@1023
   160
    	    WARN( _("Unable to read disc table of contents (error %04x)"), status );
nkeynes@1023
   161
    	}
nkeynes@1097
   162
    	cdrom_disc_clear_toc(disc);
nkeynes@1097
   163
    	return FALSE;
nkeynes@1023
   164
    }
nkeynes@1023
   165
}
nkeynes@1023
   166
nkeynes@1097
   167
static gboolean cdrom_disc_scsi_check_media( cdrom_disc_t disc )
nkeynes@1023
   168
{
nkeynes@1023
   169
	if( SCSI_TRANSPORT(disc)->media_changed(disc) ) {
nkeynes@1097
   170
		cdrom_disc_scsi_read_toc(disc, NULL);
nkeynes@1023
   171
		return TRUE;
nkeynes@1023
   172
	} else {
nkeynes@1023
   173
		return FALSE;
nkeynes@1023
   174
	}
nkeynes@1023
   175
}
nkeynes@1023
   176
nkeynes@1097
   177
static cdrom_error_t cdrom_disc_scsi_play_audio( cdrom_disc_t disc, cdrom_lba_t lba, cdrom_count_t length )
nkeynes@1023
   178
{
nkeynes@1023
   179
    char cmd[12] = { 0xA5, 0,0,0, 0,0,0,0, 0,0,0,0 };
nkeynes@1097
   180
    cmd[2] = (lba >> 24) & 0xFF;
nkeynes@1097
   181
    cmd[3] = (lba >> 16) & 0xFF;
nkeynes@1097
   182
    cmd[4] = (lba >> 8) & 0xFF;
nkeynes@1097
   183
    cmd[5] = lba & 0xFF;
nkeynes@1023
   184
    cmd[6] = (length >> 24) & 0xFF;
nkeynes@1023
   185
    cmd[7] = (length >> 16) & 0xFF;
nkeynes@1023
   186
    cmd[8] = (length >> 8) & 0xFF;
nkeynes@1023
   187
    cmd[9] = length & 0xFF;
nkeynes@1023
   188
nkeynes@1023
   189
    return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
nkeynes@1023
   190
}
nkeynes@1023
   191
nkeynes@1023
   192
nkeynes@1097
   193
static cdrom_error_t cdrom_disc_scsi_stop_audio( cdrom_disc_t disc )
nkeynes@1023
   194
{
nkeynes@1023
   195
    uint32_t buflen = 0;
nkeynes@1023
   196
    char cmd[12] = {0x4E,0,0,0, 0,0,0,0, 0,0,0,0};
nkeynes@1023
   197
    
nkeynes@1023
   198
    return SCSI_TRANSPORT(disc)->packet_cmd( disc, cmd );
nkeynes@1023
   199
}
nkeynes@1023
   200
nkeynes@1097
   201
void cdrom_disc_scsi_init( cdrom_disc_t disc, cdrom_scsi_transport_t scsi )
nkeynes@1097
   202
{
nkeynes@1097
   203
    disc->impl_data = scsi;
nkeynes@1097
   204
    disc->source.read_sectors = cdrom_disc_scsi_read_sectors;
nkeynes@1097
   205
    disc->read_toc = cdrom_disc_scsi_read_toc;
nkeynes@1097
   206
    disc->check_media = cdrom_disc_scsi_check_media;
nkeynes@1097
   207
    disc->play_audio = cdrom_disc_scsi_play_audio;
nkeynes@1097
   208
    disc->stop_audio = cdrom_disc_scsi_stop_audio;
nkeynes@1097
   209
}
nkeynes@1023
   210
nkeynes@1097
   211
cdrom_disc_t cdrom_disc_scsi_new( const char *filename, cdrom_scsi_transport_t scsi, ERROR *err )
nkeynes@1023
   212
{
nkeynes@1097
   213
    cdrom_disc_t disc = cdrom_disc_new(filename, err);
nkeynes@1023
   214
    if( disc != NULL ) {
nkeynes@1097
   215
        /* Initialize */
nkeynes@1097
   216
        cdrom_disc_scsi_init( disc, scsi );
nkeynes@1097
   217
        cdrom_disc_read_toc(disc, err);
nkeynes@1023
   218
    }
nkeynes@1023
   219
    return disc;
nkeynes@1071
   220
} 
nkeynes@1097
   221
nkeynes@1097
   222
cdrom_disc_t cdrom_disc_scsi_new_file( FILE *f, const char *filename, cdrom_scsi_transport_t scsi, ERROR *err )
nkeynes@1097
   223
{
nkeynes@1097
   224
    cdrom_disc_t disc = cdrom_disc_new(filename, err);
nkeynes@1097
   225
    if( disc != NULL ) {
nkeynes@1097
   226
        /* Initialize */
nkeynes@1097
   227
        disc->base_source = file_sector_source_new( f, SECTOR_UNKNOWN, 0, 0, TRUE );
nkeynes@1097
   228
        if( disc->base_source != NULL ) {
nkeynes@1097
   229
            sector_source_ref( disc->base_source );
nkeynes@1097
   230
            cdrom_disc_scsi_init( disc, scsi );
nkeynes@1097
   231
            cdrom_disc_read_toc(disc, err);
nkeynes@1097
   232
        } else {
nkeynes@1097
   233
            cdrom_disc_unref(disc);
nkeynes@1097
   234
            disc = NULL;
nkeynes@1097
   235
        }
nkeynes@1097
   236
    }
nkeynes@1097
   237
    return disc;
nkeynes@1097
   238
}
.