2008-11-29 00:56:26 -05:00
|
|
|
|
/* Common Multimedia Command (MMC) routines.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010
|
|
|
|
|
|
Rocky Bernstein <rocky@gnu.org>
|
2008-11-29 00:56:26 -05:00
|
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
|
# include "config.h"
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include <cdio/cdio.h>
|
|
|
|
|
|
#include <cdio/logging.h>
|
|
|
|
|
|
#include <cdio/mmc.h>
|
2010-02-06 11:59:35 -05:00
|
|
|
|
#include <cdio/mmc_cmds.h>
|
2008-11-29 00:56:26 -05:00
|
|
|
|
#include <cdio/util.h>
|
|
|
|
|
|
#include "cdio_private.h"
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_STRING_H
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_STDIO_H
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_ERRNO_H
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
2010-02-05 03:37:27 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Maps a mmc_sense_key_t into a string name.
|
|
|
|
|
|
*/
|
|
|
|
|
|
const char mmc_sense_key2str[16][40] = {
|
|
|
|
|
|
"No Sense", /**< 0 No specific Sense Key info reported */
|
|
|
|
|
|
"Recovered Error", /**< 1 Completed ok with recovery */
|
|
|
|
|
|
"Not Ready", /**< 2 */
|
|
|
|
|
|
"Medium Error", /**< 3 */
|
|
|
|
|
|
"Hardware Error", /**< 4 */
|
|
|
|
|
|
"Illegal Request", /**< 5 */
|
|
|
|
|
|
"Unit Attention", /**< 6 */
|
|
|
|
|
|
"Data Protect", /**< 7 */
|
2010-02-05 07:40:31 -05:00
|
|
|
|
"Blank Check", /**< 8 */
|
|
|
|
|
|
"Vendor Specific", /**< 9 */
|
2010-02-05 03:37:27 -05:00
|
|
|
|
"Copy aborted", /**< A */
|
|
|
|
|
|
"Aborted Command", /**< B */
|
2010-02-05 07:40:31 -05:00
|
|
|
|
"Obsolete", /**< C */
|
|
|
|
|
|
"Unknown - 13", /**< D */
|
|
|
|
|
|
"Unknown - 14", /**< E */
|
|
|
|
|
|
"Unknown - 15", /**< F */
|
2010-02-05 03:37:27 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
2008-11-29 00:56:26 -05:00
|
|
|
|
/** The below variables are trickery to force enum symbol values to be
|
|
|
|
|
|
recorded in debug symbol tables. They are used to allow one to refer
|
|
|
|
|
|
to the enumeration value names in the typedefs above in a debugger
|
|
|
|
|
|
and debugger expressions
|
|
|
|
|
|
*/
|
|
|
|
|
|
cdio_mmc_feature_t debug_cdio_mmc_feature;
|
|
|
|
|
|
cdio_mmc_feature_interface_t debug_cdio_mmc_feature_interface;
|
|
|
|
|
|
cdio_mmc_feature_profile_t debug_cdio_mmc_feature_profile;
|
|
|
|
|
|
cdio_mmc_get_conf_t debug_cdio_mmc_get_conf;
|
|
|
|
|
|
cdio_mmc_gpcmd_t debug_cdio_mmc_gpcmd;
|
|
|
|
|
|
cdio_mmc_read_sub_state_t debug_cdio_mmc_read_sub_state;
|
|
|
|
|
|
cdio_mmc_read_cd_type_t debug_cdio_mmc_read_cd_type;
|
|
|
|
|
|
cdio_mmc_readtoc_t debug_cdio_mmc_readtoc;
|
|
|
|
|
|
cdio_mmc_mode_page_t debug_cdio_mmc_mode_page;
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
|
|
MMC CdIo Operations which a driver may use.
|
|
|
|
|
|
These are not accessible directly.
|
|
|
|
|
|
|
|
|
|
|
|
Most of these routines just pick out the cdio pointer and call the
|
|
|
|
|
|
corresponding publically-accessible routine.
|
|
|
|
|
|
*************************************************************************/
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
The maximum value in milliseconds that we will wait on an MMC
|
|
|
|
|
|
command.
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
uint32_t mmc_timeout_ms = MMC_TIMEOUT_DEFAULT;
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
The maximum value in milliseconds that we will wait on an MMC read
|
|
|
|
|
|
command.
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
uint32_t mmc_read_timeout_ms = MMC_READ_TIMEOUT_DEFAULT;
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Read Audio Subchannel information
|
|
|
|
|
|
|
|
|
|
|
|
@param p_user_data the CD object to be acted upon.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
audio_read_subchannel_mmc ( void *p_user_data, cdio_subchannel_t *p_subchannel)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return mmc_audio_read_subchannel(p_env->cdio, p_subchannel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
Return a string containing the name of the audio state as returned from
|
|
|
|
|
|
the Q_SUBCHANNEL.
|
|
|
|
|
|
*/
|
|
|
|
|
|
const char *mmc_audio_state2str( uint8_t i_audio_state )
|
|
|
|
|
|
{
|
|
|
|
|
|
switch(i_audio_state) {
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_INVALID:
|
|
|
|
|
|
return "invalid";
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_PLAY:
|
|
|
|
|
|
return "playing";
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_PAUSED:
|
|
|
|
|
|
return "paused";
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_COMPLETED:
|
|
|
|
|
|
return "completed";
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_ERROR:
|
|
|
|
|
|
return "error";
|
|
|
|
|
|
case CDIO_MMC_READ_SUB_ST_NO_STATUS:
|
|
|
|
|
|
return "no status";
|
|
|
|
|
|
default:
|
|
|
|
|
|
return "unknown";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Get the block size for subsequest read requests, via MMC.
|
|
|
|
|
|
@return the blocksize if > 0; error if <= 0
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
get_blocksize_mmc (void *p_user_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return mmc_get_blocksize(p_env->cdio);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Get the lsn of the end of the CD (via MMC).
|
|
|
|
|
|
*/
|
|
|
|
|
|
lsn_t
|
|
|
|
|
|
get_disc_last_lsn_mmc (void *p_user_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return CDIO_INVALID_LSN;
|
|
|
|
|
|
return mmc_get_disc_last_lsn(p_env->cdio);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
|
get_drive_cap_mmc (const void *p_user_data,
|
|
|
|
|
|
/*out*/ cdio_drive_read_cap_t *p_read_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_write_cap_t *p_write_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_misc_cap_t *p_misc_cap)
|
|
|
|
|
|
{
|
|
|
|
|
|
const generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
mmc_get_drive_cap( p_env->cdio,
|
|
|
|
|
|
p_read_cap, p_write_cap, p_misc_cap );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Find out if media has changed since the last call. @param
|
|
|
|
|
|
p_user_data the environment of the CD object to be acted upon.
|
|
|
|
|
|
@return 1 if media has changed since last call, 0 if not. Error
|
|
|
|
|
|
return codes are the same as driver_return_code_t
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
int
|
|
|
|
|
|
get_media_changed_mmc (const void *p_user_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
const generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
return mmc_get_media_changed( p_env->cdio );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
|
get_mcn_mmc (const void *p_user_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
const generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
return mmc_get_mcn( p_env->cdio );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
get_tray_status (const void *p_user_data)
|
|
|
|
|
|
{
|
|
|
|
|
|
const generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
return mmc_get_tray_status( p_env->cdio );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*! Read sectors using SCSI-MMC GPCMD_READ_CD.
|
|
|
|
|
|
Can read only up to 25 blocks.
|
|
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
read_data_sectors_mmc ( void *p_user_data, void *p_buf,
|
|
|
|
|
|
lsn_t i_lsn, uint16_t i_blocksize,
|
|
|
|
|
|
uint32_t i_blocks )
|
|
|
|
|
|
{
|
|
|
|
|
|
const generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
return mmc_read_data_sectors( p_env->cdio, p_buf, i_lsn, i_blocksize,
|
|
|
|
|
|
i_blocks );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Set read blocksize (via MMC)
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
set_blocksize_mmc (void *p_user_data, uint16_t i_blocksize)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return mmc_set_blocksize(p_env->cdio, i_blocksize);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/** Set the drive speed Set the drive speed in K bytes per second. (via
|
|
|
|
|
|
MMC).
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
set_speed_mmc (void *p_user_data, int i_speed)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return mmc_set_speed( p_env->cdio, i_speed );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Set the drive speed in CD-ROM speed units (via MMC).
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
set_drive_speed_mmc (void *p_user_data, int i_Kbs_speed)
|
|
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return mmc_set_drive_speed( p_env->cdio, i_Kbs_speed );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Get the output port volumes and port selections used on AUDIO PLAY
|
2008-11-29 00:56:26 -05:00
|
|
|
|
commands via a MMC MODE SENSE command using the CD Audio Control
|
|
|
|
|
|
Page.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_audio_get_volume( CdIo_t *p_cdio, /*out*/ mmc_audio_volume_t *p_volume )
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t buf[16];
|
|
|
|
|
|
int i_rc = mmc_mode_sense(p_cdio, buf, sizeof(buf), CDIO_MMC_AUDIO_CTL_PAGE);
|
|
|
|
|
|
|
|
|
|
|
|
if ( DRIVER_OP_SUCCESS == i_rc ) {
|
|
|
|
|
|
p_volume->port[0].selection = 0xF & buf[8];
|
|
|
|
|
|
p_volume->port[0].volume = buf[9];
|
|
|
|
|
|
p_volume->port[1].selection = 0xF & buf[10];
|
|
|
|
|
|
p_volume->port[1].volume = buf[11];
|
|
|
|
|
|
p_volume->port[2].selection = 0xF & buf[12];
|
|
|
|
|
|
p_volume->port[2].volume = buf[13];
|
|
|
|
|
|
p_volume->port[3].selection = 0xF & buf[14];
|
|
|
|
|
|
p_volume->port[3].volume = buf[15];
|
|
|
|
|
|
return DRIVER_OP_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
return i_rc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
On input a MODE_SENSE command was issued and we have the results
|
|
|
|
|
|
in p. We interpret this and return a bit mask set according to the
|
|
|
|
|
|
capabilities.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
void
|
|
|
|
|
|
mmc_get_drive_cap_buf(const uint8_t *p,
|
|
|
|
|
|
/*out*/ cdio_drive_read_cap_t *p_read_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_write_cap_t *p_write_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_misc_cap_t *p_misc_cap)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Reader */
|
|
|
|
|
|
if (p[2] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_R;
|
|
|
|
|
|
if (p[2] & 0x02) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_RW;
|
|
|
|
|
|
if (p[2] & 0x08) *p_read_cap |= CDIO_DRIVE_CAP_READ_DVD_ROM;
|
|
|
|
|
|
if (p[4] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_AUDIO;
|
|
|
|
|
|
if (p[4] & 0x10) *p_read_cap |= CDIO_DRIVE_CAP_READ_MODE2_FORM1;
|
|
|
|
|
|
if (p[4] & 0x20) *p_read_cap |= CDIO_DRIVE_CAP_READ_MODE2_FORM2;
|
|
|
|
|
|
if (p[5] & 0x01) *p_read_cap |= CDIO_DRIVE_CAP_READ_CD_DA;
|
|
|
|
|
|
if (p[5] & 0x10) *p_read_cap |= CDIO_DRIVE_CAP_READ_C2_ERRS;
|
|
|
|
|
|
if (p[5] & 0x20) *p_read_cap |= CDIO_DRIVE_CAP_READ_ISRC;
|
|
|
|
|
|
if (p[5] & 0x40) *p_read_cap |= CDIO_DRIVE_CAP_READ_MCN;
|
|
|
|
|
|
|
|
|
|
|
|
/* Writer */
|
|
|
|
|
|
if (p[3] & 0x01) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_R;
|
|
|
|
|
|
if (p[3] & 0x02) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_CD_RW;
|
|
|
|
|
|
if (p[3] & 0x10) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_R;
|
|
|
|
|
|
if (p[3] & 0x20) *p_write_cap |= CDIO_DRIVE_CAP_WRITE_DVD_RAM;
|
|
|
|
|
|
if (p[4] & 0x80) *p_misc_cap |= CDIO_DRIVE_CAP_WRITE_BURN_PROOF;
|
|
|
|
|
|
|
|
|
|
|
|
/* Misc */
|
|
|
|
|
|
if (p[4] & 0x40) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_MULTI_SESSION;
|
|
|
|
|
|
if (p[6] & 0x01) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_LOCK;
|
|
|
|
|
|
if (p[6] & 0x08) *p_misc_cap |= CDIO_DRIVE_CAP_MISC_EJECT;
|
|
|
|
|
|
if (p[6] >> 5 != 0)
|
|
|
|
|
|
*p_misc_cap |= CDIO_DRIVE_CAP_MISC_CLOSE_TRAY;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Get the DVD type associated with cd object.
|
|
|
|
|
|
*/
|
|
|
|
|
|
discmode_t
|
|
|
|
|
|
mmc_get_dvd_struct_physical_private ( void *p_env,
|
|
|
|
|
|
mmc_run_cmd_fn_t run_mmc_cmd,
|
|
|
|
|
|
cdio_dvd_struct_t *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
unsigned char buf[4 + 4 * 20], *base;
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
uint8_t layer_num = s->physical.layer_num;
|
|
|
|
|
|
|
|
|
|
|
|
cdio_dvd_layer_t *layer;
|
|
|
|
|
|
|
|
|
|
|
|
if (!p_env) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if (!run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
|
|
|
|
|
|
if (layer_num >= CDIO_DVD_MAX_LAYERS)
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_DVD_STRUCTURE);
|
|
|
|
|
|
cdb.field[6] = layer_num;
|
|
|
|
|
|
cdb.field[7] = CDIO_DVD_STRUCT_PHYSICAL;
|
|
|
|
|
|
cdb.field[9] = sizeof(buf) & 0xff;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = run_mmc_cmd(p_env, mmc_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]),
|
|
|
|
|
|
&cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
sizeof(buf), &buf);
|
|
|
|
|
|
if (0 != i_status)
|
|
|
|
|
|
return CDIO_DISC_MODE_ERROR;
|
|
|
|
|
|
|
|
|
|
|
|
base = &buf[4];
|
|
|
|
|
|
layer = &s->physical.layer[layer_num];
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* place the data... really ugly, but at least we won't have to
|
|
|
|
|
|
* worry about endianess in userspace.
|
|
|
|
|
|
*/
|
|
|
|
|
|
memset(layer, 0, sizeof(*layer));
|
|
|
|
|
|
layer->book_version = base[0] & 0xf;
|
|
|
|
|
|
layer->book_type = base[0] >> 4;
|
|
|
|
|
|
layer->min_rate = base[1] & 0xf;
|
|
|
|
|
|
layer->disc_size = base[1] >> 4;
|
|
|
|
|
|
layer->layer_type = base[2] & 0xf;
|
|
|
|
|
|
layer->track_path = (base[2] >> 4) & 1;
|
|
|
|
|
|
layer->nlayers = (base[2] >> 5) & 3;
|
|
|
|
|
|
layer->track_density = base[3] & 0xf;
|
|
|
|
|
|
layer->linear_density = base[3] >> 4;
|
|
|
|
|
|
layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
|
|
|
|
|
|
layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
|
|
|
|
|
|
layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
|
|
|
|
|
|
layer->bca = base[16] >> 7;
|
|
|
|
|
|
|
|
|
|
|
|
return DRIVER_OP_SUCCESS;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Return the media catalog number MCN.
|
|
|
|
|
|
|
|
|
|
|
|
Note: string is malloc'd so caller should free() then returned
|
|
|
|
|
|
string when done with it.
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
char *
|
|
|
|
|
|
mmc_get_mcn_private ( void *p_env,
|
|
|
|
|
|
const mmc_run_cmd_fn_t run_mmc_cmd
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
char buf[28] = { 0, };
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! p_env || ! run_mmc_cmd )
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf));
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = 0x0;
|
|
|
|
|
|
cdb.field[2] = 0x40;
|
|
|
|
|
|
cdb.field[3] = CDIO_SUBCHANNEL_MEDIA_CATALOG;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = run_mmc_cmd(p_env, mmc_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]),
|
|
|
|
|
|
&cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
sizeof(buf), buf);
|
|
|
|
|
|
if(i_status == 0) {
|
|
|
|
|
|
return strdup(&buf[9]);
|
|
|
|
|
|
}
|
|
|
|
|
|
return NULL;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Read cdtext information for a CdIo_t object .
|
|
|
|
|
|
|
|
|
|
|
|
return true on success, false on error or CD-Text information does
|
|
|
|
|
|
not exist.
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool
|
|
|
|
|
|
mmc_init_cdtext_private ( void *p_user_data,
|
|
|
|
|
|
const mmc_run_cmd_fn_t run_mmc_cmd,
|
|
|
|
|
|
set_cdtext_field_fn_t set_cdtext_field_fn
|
|
|
|
|
|
)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
generic_img_private_t *p_env = p_user_data;
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
unsigned char wdata[5000] = { 0, };
|
|
|
|
|
|
int i_status, i_errno;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! p_env || ! run_mmc_cmd || p_env->b_cdtext_error )
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
/* Operation code */
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
|
|
|
|
|
|
|
|
|
|
|
|
/* Setup to read header, to get length of data */
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8(cdb.field, 4);
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = CDIO_CDROM_MSF;
|
|
|
|
|
|
/* Format */
|
|
|
|
|
|
cdb.field[2] = CDIO_MMC_READTOC_FMT_CDTEXT;
|
|
|
|
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* We may need to give CD-Text a little more time to complete. */
|
|
|
|
|
|
/* First off, just try and read the size */
|
|
|
|
|
|
i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]),
|
|
|
|
|
|
&cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
4, &wdata);
|
|
|
|
|
|
|
|
|
|
|
|
if (i_status != 0) {
|
|
|
|
|
|
cdio_info ("CD-Text read failed for header: %s\n", strerror(errno));
|
|
|
|
|
|
i_errno = errno;
|
|
|
|
|
|
p_env->b_cdtext_error = true;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
/* Now read the CD-Text data */
|
|
|
|
|
|
int i_cdtext = CDIO_MMC_GET_LEN16(wdata);
|
|
|
|
|
|
|
|
|
|
|
|
if (i_cdtext > sizeof(wdata)) i_cdtext = sizeof(wdata);
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_cdtext);
|
|
|
|
|
|
i_status = run_mmc_cmd (p_env, mmc_read_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]),
|
|
|
|
|
|
&cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
i_cdtext, &wdata);
|
|
|
|
|
|
if (i_status != 0) {
|
|
|
|
|
|
cdio_info ("CD-Text read for text failed: %s\n", strerror(errno));
|
|
|
|
|
|
i_errno = errno;
|
|
|
|
|
|
p_env->b_cdtext_error = true;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
p_env->b_cdtext_init = true;
|
|
|
|
|
|
return cdtext_data_init(p_env, p_env->i_first_track, wdata, i_cdtext-2,
|
|
|
|
|
|
set_cdtext_field_fn);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_set_blocksize_private ( void *p_env,
|
|
|
|
|
|
const mmc_run_cmd_fn_t run_mmc_cmd,
|
|
|
|
|
|
uint16_t i_blocksize)
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
|
|
|
|
|
|
struct
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t reserved1;
|
|
|
|
|
|
uint8_t medium;
|
|
|
|
|
|
uint8_t reserved2;
|
|
|
|
|
|
uint8_t block_desc_length;
|
|
|
|
|
|
uint8_t density;
|
|
|
|
|
|
uint8_t number_of_blocks_hi;
|
|
|
|
|
|
uint8_t number_of_blocks_med;
|
|
|
|
|
|
uint8_t number_of_blocks_lo;
|
|
|
|
|
|
uint8_t reserved3;
|
|
|
|
|
|
uint8_t block_length_hi;
|
|
|
|
|
|
uint8_t block_length_med;
|
|
|
|
|
|
uint8_t block_length_lo;
|
|
|
|
|
|
} mh;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! p_env ) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if ( ! run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
|
|
|
|
|
|
memset (&mh, 0, sizeof (mh));
|
|
|
|
|
|
mh.block_desc_length = 0x08;
|
|
|
|
|
|
mh.block_length_hi = (i_blocksize >> 16) & 0xff;
|
|
|
|
|
|
mh.block_length_med = (i_blocksize >> 8) & 0xff;
|
|
|
|
|
|
mh.block_length_lo = (i_blocksize >> 0) & 0xff;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SELECT_6);
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = 1 << 4;
|
|
|
|
|
|
cdb.field[4] = 12;
|
|
|
|
|
|
|
|
|
|
|
|
return run_mmc_cmd (p_env, mmc_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]), &cdb,
|
|
|
|
|
|
SCSI_MMC_DATA_WRITE, sizeof(mh), &mh);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************
|
|
|
|
|
|
User-accessible Operations.
|
|
|
|
|
|
************************************************************/
|
2010-01-28 19:38:34 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Read Audio Subchannel information
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_audio_read_subchannel (CdIo_t *p_cdio, cdio_subchannel_t *p_subchannel)
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb;
|
|
|
|
|
|
driver_return_code_t i_rc;
|
|
|
|
|
|
cdio_mmc_subchannel_t mmc_subchannel;
|
|
|
|
|
|
|
|
|
|
|
|
if (!p_cdio) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
|
|
|
|
|
|
memset(&mmc_subchannel, 0, sizeof(mmc_subchannel));
|
|
|
|
|
|
mmc_subchannel.format = CDIO_CDROM_MSF;
|
|
|
|
|
|
memset(&cdb, 0, sizeof(mmc_cdb_t));
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(cdio_mmc_subchannel_t));
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = CDIO_CDROM_MSF;
|
|
|
|
|
|
cdb.field[2] = 0x40; /* subq */
|
|
|
|
|
|
cdb.field[3] = CDIO_SUBCHANNEL_CURRENT_POSITION;
|
|
|
|
|
|
cdb.field[6] = 0; /* track number (only in isrc mode, ignored) */
|
|
|
|
|
|
|
|
|
|
|
|
i_rc = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
sizeof(cdio_mmc_subchannel_t), &mmc_subchannel);
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_rc) {
|
|
|
|
|
|
p_subchannel->format = mmc_subchannel.format;
|
|
|
|
|
|
p_subchannel->audio_status = mmc_subchannel.audio_status;
|
|
|
|
|
|
p_subchannel->address = mmc_subchannel.address;
|
|
|
|
|
|
p_subchannel->control = mmc_subchannel.control;
|
|
|
|
|
|
p_subchannel->track = mmc_subchannel.track;
|
|
|
|
|
|
p_subchannel->index = mmc_subchannel.index;
|
|
|
|
|
|
p_subchannel->abs_addr.m = cdio_to_bcd8(mmc_subchannel.abs_addr[1]);
|
|
|
|
|
|
p_subchannel->abs_addr.s = cdio_to_bcd8(mmc_subchannel.abs_addr[2]);
|
|
|
|
|
|
p_subchannel->abs_addr.f = cdio_to_bcd8(mmc_subchannel.abs_addr[3]);
|
|
|
|
|
|
p_subchannel->rel_addr.m = cdio_to_bcd8(mmc_subchannel.rel_addr[1]);
|
|
|
|
|
|
p_subchannel->rel_addr.s = cdio_to_bcd8(mmc_subchannel.rel_addr[2]);
|
|
|
|
|
|
p_subchannel->rel_addr.f = cdio_to_bcd8(mmc_subchannel.rel_addr[3]);
|
|
|
|
|
|
}
|
|
|
|
|
|
return i_rc;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
Get the block size used in read requests, via MMC (e.g. READ_10,
|
|
|
|
|
|
READ_MSF, ...)
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return the blocksize if > 0; error if <= 0
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
mmc_get_blocksize ( CdIo_t *p_cdio)
|
|
|
|
|
|
{
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t buf[255] = { 0, };
|
|
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
|
|
|
|
|
|
/* First try using the 6-byte MODE SENSE command. */
|
|
|
|
|
|
i_status = mmc_mode_sense_6(p_cdio, buf, sizeof(buf),
|
|
|
|
|
|
CDIO_MMC_R_W_ERROR_PAGE);
|
|
|
|
|
|
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_status && buf[3]>=8) {
|
|
|
|
|
|
p = &buf[4+5];
|
|
|
|
|
|
return CDIO_MMC_GET_LEN16(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Next try using the 10-byte MODE SENSE command. */
|
|
|
|
|
|
i_status = mmc_mode_sense_10(p_cdio, buf, sizeof(buf),
|
|
|
|
|
|
CDIO_MMC_R_W_ERROR_PAGE);
|
|
|
|
|
|
p = &buf[6];
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_status && CDIO_MMC_GET_LEN16(p)>=8) {
|
|
|
|
|
|
return CDIO_MMC_GET_LEN16(p);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef IS_THIS_CORRECT
|
|
|
|
|
|
/* Lastly try using the READ CAPACITY command. */
|
|
|
|
|
|
{
|
|
|
|
|
|
lba_t lba = 0;
|
|
|
|
|
|
uint16_t i_blocksize;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_read_capacity(p_cdio, &lba, &i_blocksize);
|
|
|
|
|
|
if ( DRIVER_OP_SUCCESS == i_status )
|
|
|
|
|
|
return i_blocksize;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Return the number of length in bytes of the Command Descriptor
|
|
|
|
|
|
buffer (CDB) for a given MMC command. The length will be
|
|
|
|
|
|
either 6, 10, or 12.
|
|
|
|
|
|
*/
|
|
|
|
|
|
uint8_t
|
|
|
|
|
|
mmc_get_cmd_len(uint8_t scsi_cmd)
|
|
|
|
|
|
{
|
|
|
|
|
|
static const uint8_t scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10};
|
|
|
|
|
|
return scsi_cdblen[((scsi_cmd >> 5) & 7)];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 19:38:34 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Detects if a disc (CD or DVD) is erasable or not.
|
2010-01-31 07:46:48 -05:00
|
|
|
|
|
2010-01-28 19:38:34 -05:00
|
|
|
|
@param p_user_data the CD object to be acted upon.
|
2010-01-31 07:46:48 -05:00
|
|
|
|
|
|
|
|
|
|
@param i_status, if not NULL, on return will be set indicate whether
|
|
|
|
|
|
the operation was a success (DRIVER_OP_SUCCESS) or if not to some
|
|
|
|
|
|
other value.
|
|
|
|
|
|
|
2010-01-28 19:38:34 -05:00
|
|
|
|
@return true if the disc is detected as erasable (rewritable), false
|
|
|
|
|
|
otherwise.
|
|
|
|
|
|
*/
|
|
|
|
|
|
bool
|
2010-01-31 07:46:48 -05:00
|
|
|
|
mmc_get_disc_erasable( const CdIo_t *p_cdio,
|
|
|
|
|
|
driver_return_code_t *opt_i_status ) {
|
2010-01-28 19:38:34 -05:00
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
uint8_t buf[42] = { 0, };
|
2010-01-31 07:46:48 -05:00
|
|
|
|
driver_return_code_t i_status;
|
|
|
|
|
|
|
2010-01-28 19:38:34 -05:00
|
|
|
|
CDIO_MMC_SET_COMMAND (cdb.field, CDIO_MMC_GPCMD_READ_DISC_INFO);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8 (cdb.field, sizeof(buf));
|
|
|
|
|
|
|
2010-01-31 07:46:48 -05:00
|
|
|
|
i_status = mmc_run_cmd (p_cdio, 0, &cdb, SCSI_MMC_DATA_READ,
|
2010-01-28 19:38:34 -05:00
|
|
|
|
sizeof(buf), &buf);
|
2010-01-31 07:46:48 -05:00
|
|
|
|
if (opt_i_status != NULL) *opt_i_status = i_status;
|
|
|
|
|
|
return (DRIVER_OP_SUCCESS == i_status) ?
|
|
|
|
|
|
((buf[2] & 0x10) ? true : false)
|
|
|
|
|
|
: false;
|
2010-01-28 19:38:34 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Return the size of the CD in logical block address (LBA) units.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
@return the lsn. On error 0 or CDIO_INVALD_LSN.
|
|
|
|
|
|
*/
|
|
|
|
|
|
lsn_t
|
|
|
|
|
|
mmc_get_disc_last_lsn ( const CdIo_t *p_cdio )
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
uint8_t buf[12] = { 0, };
|
|
|
|
|
|
|
|
|
|
|
|
lsn_t retval = 0;
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
|
|
|
|
|
|
/* Operation code */
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = 0; /* lba; msf: 0x2 */
|
|
|
|
|
|
|
|
|
|
|
|
/* Format */
|
|
|
|
|
|
cdb.field[2] = CDIO_MMC_READTOC_FMT_TOC;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_START_TRACK(cdb.field, CDIO_CDROM_LEADOUT_TRACK);
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf));
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
sizeof(buf), buf);
|
|
|
|
|
|
|
|
|
|
|
|
if (i_status) return CDIO_INVALID_LSN;
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
int i;
|
|
|
|
|
|
for (i = 8; i < 12; i++) {
|
|
|
|
|
|
retval <<= 8;
|
|
|
|
|
|
retval += buf[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Return the discmode as reported by the SCSI-MMC Read (FULL) TOC
|
|
|
|
|
|
command.
|
|
|
|
|
|
|
|
|
|
|
|
Information was obtained from Section 5.1.13 (Read TOC/PMA/ATIP)
|
|
|
|
|
|
pages 56-62 from the MMC draft specification, revision 10a
|
|
|
|
|
|
at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See
|
|
|
|
|
|
especially tables 72, 73 and 75.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
discmode_t
|
|
|
|
|
|
mmc_get_discmode( const CdIo_t *p_cdio )
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t buf[14] = { 0, };
|
|
|
|
|
|
mmc_cdb_t cdb;
|
|
|
|
|
|
|
|
|
|
|
|
memset(&cdb, 0, sizeof(mmc_cdb_t));
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf));
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = CDIO_CDROM_MSF; /* The MMC-5 spec may require this. */
|
|
|
|
|
|
cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC;
|
|
|
|
|
|
|
|
|
|
|
|
mmc_run_cmd(p_cdio, 2000, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), buf);
|
|
|
|
|
|
if (buf[7] == 0xA0) {
|
|
|
|
|
|
if (buf[13] == 0x00) {
|
|
|
|
|
|
if (buf[5] & 0x04)
|
|
|
|
|
|
return CDIO_DISC_MODE_CD_DATA;
|
|
|
|
|
|
else
|
|
|
|
|
|
return CDIO_DISC_MODE_CD_DA;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (buf[13] == 0x10)
|
|
|
|
|
|
return CDIO_DISC_MODE_CD_I;
|
|
|
|
|
|
else if (buf[13] == 0x20)
|
|
|
|
|
|
return CDIO_DISC_MODE_CD_XA;
|
|
|
|
|
|
}
|
|
|
|
|
|
return CDIO_DISC_MODE_NO_INFO;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Get drive capabilities for a device.
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return the drive capabilities.
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
void
|
|
|
|
|
|
mmc_get_drive_cap (CdIo_t *p_cdio,
|
|
|
|
|
|
/*out*/ cdio_drive_read_cap_t *p_read_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_write_cap_t *p_write_cap,
|
|
|
|
|
|
/*out*/ cdio_drive_misc_cap_t *p_misc_cap)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* Largest buffer size we use. */
|
|
|
|
|
|
#define BUF_MAX 2048
|
|
|
|
|
|
uint8_t buf[BUF_MAX] = { 0, };
|
|
|
|
|
|
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
uint16_t i_data = BUF_MAX;
|
|
|
|
|
|
int page = CDIO_MMC_ALL_PAGES;
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! p_cdio ) return;
|
|
|
|
|
|
retry:
|
|
|
|
|
|
|
|
|
|
|
|
/* In the first run we run MODE SENSE 10 we are trying to get the
|
|
|
|
|
|
length of the data features. */
|
|
|
|
|
|
i_status = mmc_mode_sense_10(p_cdio, buf, 8, CDIO_MMC_ALL_PAGES);
|
|
|
|
|
|
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_status) {
|
|
|
|
|
|
uint16_t i_data_try = (uint16_t) CDIO_MMC_GET_LEN16(buf);
|
|
|
|
|
|
if (i_data_try < BUF_MAX) i_data = i_data_try;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Now try getting all features with length set above, possibly
|
|
|
|
|
|
truncated or the default length if we couldn't get the proper
|
|
|
|
|
|
length. */
|
|
|
|
|
|
i_status = mmc_mode_sense_10(p_cdio, buf, i_data, CDIO_MMC_ALL_PAGES);
|
|
|
|
|
|
if (0 != i_status && CDIO_MMC_CAPABILITIES_PAGE != page) {
|
|
|
|
|
|
page = CDIO_MMC_CAPABILITIES_PAGE;
|
|
|
|
|
|
goto retry;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_status) {
|
|
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
uint8_t *p_max = buf + 256;
|
|
|
|
|
|
|
|
|
|
|
|
*p_read_cap = 0;
|
|
|
|
|
|
*p_write_cap = 0;
|
|
|
|
|
|
*p_misc_cap = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* set to first sense mask, and then walk through the masks */
|
|
|
|
|
|
p = buf + 8;
|
|
|
|
|
|
while( (p < &(buf[2+i_data])) && (p < p_max) ) {
|
|
|
|
|
|
uint8_t which_page;
|
|
|
|
|
|
|
|
|
|
|
|
which_page = p[0] & 0x3F;
|
|
|
|
|
|
switch( which_page )
|
|
|
|
|
|
{
|
|
|
|
|
|
case CDIO_MMC_AUDIO_CTL_PAGE:
|
|
|
|
|
|
case CDIO_MMC_R_W_ERROR_PAGE:
|
|
|
|
|
|
case CDIO_MMC_CDR_PARMS_PAGE:
|
|
|
|
|
|
/* Don't handle these yet. */
|
|
|
|
|
|
break;
|
|
|
|
|
|
case CDIO_MMC_CAPABILITIES_PAGE:
|
|
|
|
|
|
mmc_get_drive_cap_buf(p, p_read_cap, p_write_cap, p_misc_cap);
|
|
|
|
|
|
break;
|
|
|
|
|
|
default: ;
|
|
|
|
|
|
}
|
|
|
|
|
|
p += (p[1] + 2);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cdio_info("%s: %s\n", "error in MODE_SELECT", strerror(errno));
|
|
|
|
|
|
*p_read_cap = CDIO_DRIVE_CAP_ERROR;
|
|
|
|
|
|
*p_write_cap = CDIO_DRIVE_CAP_ERROR;
|
|
|
|
|
|
*p_misc_cap = CDIO_DRIVE_CAP_ERROR;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Get the MMC level supported by the device.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
cdio_mmc_level_t
|
|
|
|
|
|
mmc_get_drive_mmc_cap(CdIo_t *p_cdio)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t buf[256] = { 0, };
|
|
|
|
|
|
uint8_t len;
|
|
|
|
|
|
int rc = mmc_mode_sense(p_cdio, buf, sizeof(buf),
|
|
|
|
|
|
CDIO_MMC_CAPABILITIES_PAGE);
|
|
|
|
|
|
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS != rc) {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_NONE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
len = buf[1];
|
|
|
|
|
|
if (16 > len) {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_WEIRD;
|
|
|
|
|
|
} else if (28 <= len) {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_3;
|
|
|
|
|
|
} else if (24 <= len) {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_2;
|
|
|
|
|
|
printf("MMC 2");
|
|
|
|
|
|
} else if (20 <= len) {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return CDIO_MMC_LEVEL_WEIRD;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Get the DVD type associated with cd object.
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return the DVD discmode.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
discmode_t
|
|
|
|
|
|
mmc_get_dvd_struct_physical ( const CdIo_t *p_cdio, cdio_dvd_struct_t *s)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ! p_cdio ) return -2;
|
|
|
|
|
|
return
|
|
|
|
|
|
mmc_get_dvd_struct_physical_private (p_cdio->env,
|
|
|
|
|
|
p_cdio->op.run_mmc_cmd,
|
|
|
|
|
|
s);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Get the CD-ROM hardware info via a MMC INQUIRY command.
|
|
|
|
|
|
False is returned if we had an error getting the information.
|
2010-01-28 05:06:15 -05:00
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return true if we were able to get hardware info, false if we had
|
|
|
|
|
|
an error.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
bool
|
|
|
|
|
|
mmc_get_hwinfo ( const CdIo_t *p_cdio,
|
|
|
|
|
|
/*out*/ cdio_hwinfo_t *hw_info )
|
|
|
|
|
|
{
|
|
|
|
|
|
int i_status; /* Result of MMC command */
|
|
|
|
|
|
char buf[36] = { 0, }; /* Place to hold returned data */
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Block */
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY);
|
|
|
|
|
|
cdb.field[4] = sizeof(buf);
|
|
|
|
|
|
|
|
|
|
|
|
if (! p_cdio || ! hw_info ) return false;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms,
|
|
|
|
|
|
&cdb, SCSI_MMC_DATA_READ,
|
|
|
|
|
|
sizeof(buf), &buf);
|
|
|
|
|
|
if (i_status == 0) {
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(hw_info->psz_vendor,
|
|
|
|
|
|
buf + 8,
|
|
|
|
|
|
sizeof(hw_info->psz_vendor)-1);
|
|
|
|
|
|
hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0';
|
|
|
|
|
|
memcpy(hw_info->psz_model,
|
|
|
|
|
|
buf + 8 + CDIO_MMC_HW_VENDOR_LEN,
|
|
|
|
|
|
sizeof(hw_info->psz_model)-1);
|
|
|
|
|
|
hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0';
|
|
|
|
|
|
memcpy(hw_info->psz_revision,
|
|
|
|
|
|
buf + 8 + CDIO_MMC_HW_VENDOR_LEN + CDIO_MMC_HW_MODEL_LEN,
|
|
|
|
|
|
sizeof(hw_info->psz_revision)-1);
|
|
|
|
|
|
hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0';
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Find out if media has changed since the last call.
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return 1 if media has changed since last call, 0 if not. Error
|
|
|
|
|
|
return codes are the same as driver_return_code_t
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
int mmc_get_media_changed(const CdIo_t *p_cdio)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t status_buf[2];
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_get_event_status(p_cdio, status_buf);
|
|
|
|
|
|
if (i_status != DRIVER_OP_SUCCESS)
|
|
|
|
|
|
return i_status;
|
|
|
|
|
|
return (status_buf[0] & 0x02) ? 1 : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Get the media catalog number (MCN) from the CD via MMC.
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return the media catalog number r NULL if there is none or we
|
|
|
|
|
|
don't have the ability to get it.
|
|
|
|
|
|
|
|
|
|
|
|
Note: string is malloc'd so caller has to free() the returned
|
|
|
|
|
|
string when done with it.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
char *
|
|
|
|
|
|
mmc_get_mcn ( const CdIo_t *p_cdio )
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ! p_cdio ) return NULL;
|
|
|
|
|
|
return mmc_get_mcn_private (p_cdio->env, p_cdio->op.run_mmc_cmd );
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Find out if media tray is open or closed.
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return 1 if media is open, 0 if closed. Error
|
|
|
|
|
|
return codes are the same as driver_return_code_t
|
2010-01-28 05:06:15 -05:00
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
int mmc_get_tray_status(const CdIo_t *p_cdio)
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t status_buf[2];
|
|
|
|
|
|
int i_status;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_get_event_status(p_cdio, status_buf);
|
|
|
|
|
|
if (i_status != DRIVER_OP_SUCCESS)
|
|
|
|
|
|
return i_status;
|
|
|
|
|
|
return (status_buf[1] & 0x01) ? 1 : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 19:38:34 -05:00
|
|
|
|
/* Added in version 0.83 by scdbackup */
|
|
|
|
|
|
/**
|
2010-02-05 13:50:07 -05:00
|
|
|
|
Obtain the SCSI sense reply of the most-recently-performed MMC command.
|
|
|
|
|
|
These bytes give an indication of possible problems which occured in
|
|
|
|
|
|
the drive while the command was performed. With some commands they tell
|
|
|
|
|
|
about the current state of the drive (e.g. 00h TEST UNIT READY).
|
|
|
|
|
|
@param p_cdio CD structure set by cdio_open().
|
|
|
|
|
|
|
|
|
|
|
|
@param sense returns the sense bytes received from the drive.
|
|
|
|
|
|
This is allocated memory or NULL if no sense bytes are
|
|
|
|
|
|
available. Dispose non-NULL pointers by free() when no longer
|
|
|
|
|
|
needed. See SPC-3 4.5.3 Fixed format sense data. SCSI error
|
|
|
|
|
|
codes as of SPC-3 Annex D, MMC-5 Annex F: sense[2]&15 = Key ,
|
|
|
|
|
|
sense[12] = ASC , sense[13] = ASCQ
|
|
|
|
|
|
|
|
|
|
|
|
@return number of valid bytes in sense, 0 in case of no sense
|
|
|
|
|
|
bytes available, <0 in case of internal error.
|
|
|
|
|
|
*/
|
2010-01-28 19:38:34 -05:00
|
|
|
|
int
|
2010-02-05 07:40:31 -05:00
|
|
|
|
mmc_last_cmd_sense( const CdIo_t *p_cdio, mmc_request_sense_t **pp_sense)
|
2010-01-28 19:38:34 -05:00
|
|
|
|
{
|
|
|
|
|
|
generic_img_private_t *gen;
|
|
|
|
|
|
|
|
|
|
|
|
if (!p_cdio) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
gen = p_cdio->env;
|
2010-02-05 07:40:31 -05:00
|
|
|
|
*pp_sense = NULL;
|
2010-01-28 19:38:34 -05:00
|
|
|
|
if (gen->scsi_mmc_sense_valid <= 0)
|
|
|
|
|
|
return 0;
|
2010-02-05 07:40:31 -05:00
|
|
|
|
*pp_sense = calloc(1, gen->scsi_mmc_sense_valid);
|
|
|
|
|
|
if (*pp_sense == NULL)
|
2010-01-28 19:38:34 -05:00
|
|
|
|
return DRIVER_OP_ERROR;
|
2010-02-05 07:40:31 -05:00
|
|
|
|
memcpy(*pp_sense, gen->scsi_mmc_sense, gen->scsi_mmc_sense_valid);
|
2010-01-28 19:38:34 -05:00
|
|
|
|
return gen->scsi_mmc_sense_valid;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Run a MMC command.
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
@param cdio CD structure set by cdio_open().
|
|
|
|
|
|
@param i_timeout time in milliseconds we will wait for the command
|
|
|
|
|
|
to complete. If this value is -1, use the default
|
|
|
|
|
|
time-out value.
|
|
|
|
|
|
@param buf Buffer for data, both sending and receiving
|
|
|
|
|
|
@param len Size of buffer
|
|
|
|
|
|
@param e_direction direction the transfer is to go
|
|
|
|
|
|
@param cdb CDB bytes. All values that are needed should be set on
|
|
|
|
|
|
input. We'll figure out what the right CDB length
|
|
|
|
|
|
should be.
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_run_cmd( const CdIo_t *p_cdio, unsigned int i_timeout_ms,
|
2010-02-04 04:24:43 -05:00
|
|
|
|
const mmc_cdb_t *p_cdb,
|
|
|
|
|
|
cdio_mmc_direction_t e_direction, unsigned int i_buf,
|
|
|
|
|
|
/*in/out*/ void *p_buf )
|
2008-11-29 00:56:26 -05:00
|
|
|
|
{
|
2010-02-04 04:24:43 -05:00
|
|
|
|
if (!p_cdio) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms,
|
2008-11-29 00:56:26 -05:00
|
|
|
|
mmc_get_cmd_len(p_cdb->field[0]),
|
|
|
|
|
|
p_cdb, e_direction, i_buf, p_buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Added by SukkoPera to allow CDB length to be specified manually */
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Run a Multimedia command (MMC) specifying the CDB length.
|
|
|
|
|
|
The motivation here is for example ot use in is an undocumented
|
|
|
|
|
|
debug command for LG drives (namely E7), whose length is being
|
|
|
|
|
|
miscalculated by mmc_get_cmd_len(); it doesn't follow the usual
|
|
|
|
|
|
code number to length conventions. Patch supplied by SukkoPera.
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio CD structure set by cdio_open().
|
|
|
|
|
|
@param i_timeout_ms time in milliseconds we will wait for the command
|
|
|
|
|
|
to complete.
|
|
|
|
|
|
@param p_cdb CDB bytes. All values that are needed should be set
|
|
|
|
|
|
on input.
|
|
|
|
|
|
@param i_cdb number of CDB bytes.
|
|
|
|
|
|
@param e_direction direction the transfer is to go.
|
|
|
|
|
|
@param i_buf Size of buffer
|
|
|
|
|
|
@param p_buf Buffer for data, both sending and receiving.
|
|
|
|
|
|
|
|
|
|
|
|
@return 0 if command completed successfully.
|
|
|
|
|
|
*/
|
2010-01-28 19:38:34 -05:00
|
|
|
|
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_run_cmd_len( const CdIo_t *p_cdio, unsigned int i_timeout_ms,
|
|
|
|
|
|
const mmc_cdb_t *p_cdb, unsigned int i_cdb,
|
|
|
|
|
|
cdio_mmc_direction_t e_direction, unsigned int i_buf,
|
|
|
|
|
|
/*in/out*/ void *p_buf )
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!p_cdio) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms,
|
|
|
|
|
|
i_cdb,
|
|
|
|
|
|
p_cdb, e_direction, i_buf, p_buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2010-01-28 05:06:15 -05:00
|
|
|
|
Close tray using a MMC START STOP command.
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return DRIVER_OP_SUCCESS (0) if we got the status.
|
|
|
|
|
|
return codes are the same as driver_return_code_t
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_close_tray( CdIo_t *p_cdio )
|
|
|
|
|
|
{
|
|
|
|
|
|
if (p_cdio) {
|
|
|
|
|
|
return mmc_start_stop_media(p_cdio, false, false, 0);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return DRIVER_OP_ERROR;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Eject using MMC commands. If CD-ROM is "locked" we'll unlock it.
|
|
|
|
|
|
Command is not "immediate" -- we'll wait for the command to complete.
|
|
|
|
|
|
For a more general (and lower-level) routine, @see mmc_start_stop_media.
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio the CD object to be acted upon.
|
|
|
|
|
|
@return DRIVER_OP_SUCCESS (0) if we got the status.
|
|
|
|
|
|
return codes are the same as driver_return_code_t
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_eject_media( const CdIo_t *p_cdio )
|
|
|
|
|
|
{
|
|
|
|
|
|
int i_status = 0;
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
uint8_t buf[1];
|
|
|
|
|
|
|
|
|
|
|
|
if ( ! p_cdio ) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_ALLOW_MEDIUM_REMOVAL);
|
|
|
|
|
|
|
|
|
|
|
|
i_status = p_cdio->op.run_mmc_cmd (p_cdio->env, mmc_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]), &cdb,
|
|
|
|
|
|
SCSI_MMC_DATA_WRITE, 0, &buf);
|
|
|
|
|
|
if (0 != i_status) return i_status;
|
|
|
|
|
|
|
|
|
|
|
|
return mmc_start_stop_media(p_cdio, true, false, 0);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Return a string containing the name of the given feature
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
const char *mmc_feature2str( int i_feature )
|
|
|
|
|
|
{
|
|
|
|
|
|
switch(i_feature) {
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROFILE_LIST:
|
|
|
|
|
|
return "Profile List";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CORE:
|
|
|
|
|
|
return "Core";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_MORPHING:
|
|
|
|
|
|
return "Morphing" ;
|
|
|
|
|
|
case CDIO_MMC_FEATURE_REMOVABLE_MEDIUM:
|
|
|
|
|
|
return "Removable Medium";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_WRITE_PROTECT:
|
|
|
|
|
|
return "Write Protect";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_RANDOM_READABLE:
|
|
|
|
|
|
return "Random Readable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_MULTI_READ:
|
|
|
|
|
|
return "Multi-Read";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CD_READ:
|
|
|
|
|
|
return "CD Read";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_READ:
|
|
|
|
|
|
return "DVD Read";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_RANDOM_WRITABLE:
|
|
|
|
|
|
return "Random Writable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_INCR_WRITE:
|
|
|
|
|
|
return "Incremental Streaming Writable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_SECTOR_ERASE:
|
|
|
|
|
|
return "Sector Erasable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_FORMATABLE:
|
|
|
|
|
|
return "Formattable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DEFECT_MGMT:
|
|
|
|
|
|
return "Management Ability of the Logical Unit/media system "
|
|
|
|
|
|
"to provide an apparently defect-free space.";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_WRITE_ONCE:
|
|
|
|
|
|
return "Write Once";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_RESTRICT_OVERW:
|
|
|
|
|
|
return "Restricted Overwrite";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CD_RW_CAV:
|
|
|
|
|
|
return "CD-RW CAV Write";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_MRW:
|
|
|
|
|
|
return "MRW";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_ENHANCED_DEFECT:
|
|
|
|
|
|
return "Enhanced Defect Reporting";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_PRW:
|
|
|
|
|
|
return "DVD+RW";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_PR:
|
|
|
|
|
|
return "DVD+R";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_RIGID_RES_OVERW:
|
|
|
|
|
|
return "Rigid Restricted Overwrite";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CD_TAO:
|
|
|
|
|
|
return "CD Track at Once";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CD_SAO:
|
|
|
|
|
|
return "CD Mastering (Session at Once)";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_R_RW_WRITE:
|
|
|
|
|
|
return "DVD-R/RW Write";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CD_RW_MEDIA_WRITE:
|
|
|
|
|
|
return "CD-RW Media Write Support";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_PR_2_LAYER:
|
|
|
|
|
|
return "DVD+R Double Layer";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_POWER_MGMT:
|
|
|
|
|
|
return "Initiator- and Device-directed Power Management";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_CDDA_EXT_PLAY:
|
|
|
|
|
|
return "CD Audio External Play";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_MCODE_UPGRADE:
|
|
|
|
|
|
return "Ability for the device to accept new microcode via the interface";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_TIME_OUT:
|
|
|
|
|
|
return "Ability to respond to all commands within a specific time";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_DVD_CSS:
|
|
|
|
|
|
return "Ability to perform DVD CSS/CPPM authentication via RPC";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_RT_STREAMING:
|
|
|
|
|
|
return "Ability to read and write using Initiator requested performance"
|
|
|
|
|
|
" parameters";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_LU_SN:
|
|
|
|
|
|
return "The Logical Unit Unique Identifier";
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
|
|
|
|
|
static char buf[100];
|
|
|
|
|
|
if ( 0 != (i_feature & 0xFF00) ) {
|
|
|
|
|
|
snprintf( buf, sizeof(buf),
|
|
|
|
|
|
"Vendor-specific code %x", i_feature );
|
|
|
|
|
|
} else {
|
|
|
|
|
|
snprintf( buf, sizeof(buf),
|
|
|
|
|
|
"Unknown code %x", i_feature );
|
|
|
|
|
|
}
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
2008-11-29 00:56:26 -05:00
|
|
|
|
Return a string containing the name of the given feature profile.
|
|
|
|
|
|
*/
|
|
|
|
|
|
const char *mmc_feature_profile2str( int i_feature_profile )
|
|
|
|
|
|
{
|
|
|
|
|
|
switch(i_feature_profile) {
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_NON_REMOVABLE:
|
|
|
|
|
|
return "Non-removable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_REMOVABLE:
|
|
|
|
|
|
return "disk Re-writable; with removable media";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_MO_ERASABLE:
|
|
|
|
|
|
return "Erasable Magneto-Optical disk with sector erase capability";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_MO_WRITE_ONCE:
|
|
|
|
|
|
return "Write Once Magneto-Optical write once";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_AS_MO:
|
|
|
|
|
|
return "Advance Storage Magneto-Optical";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_CD_ROM:
|
|
|
|
|
|
return "Read only Compact Disc capable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_CD_R:
|
|
|
|
|
|
return "Write once Compact Disc capable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_CD_RW:
|
|
|
|
|
|
return "CD-RW Re-writable Compact Disc capable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_ROM:
|
|
|
|
|
|
return "Read only DVD";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_R_SEQ:
|
|
|
|
|
|
return "Re-recordable DVD using Sequential recording";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_RAM:
|
|
|
|
|
|
return "Re-writable DVD";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_RW_RO:
|
|
|
|
|
|
return "Re-recordable DVD using Restricted Overwrite";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_RW_SEQ:
|
2010-01-29 04:48:38 -05:00
|
|
|
|
return "Re-recordable DVD using Sequential Recording";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_R_DL_SEQ:
|
|
|
|
|
|
return "DVD-R - Double-Layer Sequential Recording";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_R_DL_JR:
|
|
|
|
|
|
return "DVD-R - Double-layer Jump Recording";
|
2008-11-29 00:56:26 -05:00
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_PRW:
|
2010-01-29 04:48:38 -05:00
|
|
|
|
return "DVD+RW - DVD Rewritable";
|
2008-11-29 00:56:26 -05:00
|
|
|
|
case CDIO_MMC_FEATURE_RIGID_RES_OVERW:
|
|
|
|
|
|
return "Rigid Restricted Overwrite";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_PR:
|
|
|
|
|
|
return "DVD+R - DVD Recordable";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DDCD_ROM:
|
|
|
|
|
|
return "Read only DDCD";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DDCD_R:
|
|
|
|
|
|
return "DDCD-R Write only DDCD";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DDCD_RW:
|
|
|
|
|
|
return "Re-Write only DDCD";
|
2010-01-29 04:48:38 -05:00
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_RW_DL:
|
|
|
|
|
|
return "DVD+RW - Double Layer";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_DVD_PR2:
|
|
|
|
|
|
return "DVD+R Double Layer - DVD Recordable Double Layer";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_BD_ROM:
|
|
|
|
|
|
return "Blu Ray BD-ROM";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_BD_SEQ:
|
|
|
|
|
|
return "Blu Ray BD-R sequential recording";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_BD_R_RANDOM:
|
|
|
|
|
|
return "Blu Ray BD-R random recording";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_BD_RE:
|
|
|
|
|
|
return "Blu Ray BD-RE";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_HD_DVD_ROM:
|
|
|
|
|
|
return "HD-DVD-ROM";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_HD_DVD_R:
|
|
|
|
|
|
return "HD-DVD-R";
|
|
|
|
|
|
case CDIO_MMC_FEATURE_PROF_HD_DVD_RAM:
|
|
|
|
|
|
return "HD-DVD-RAM";
|
2008-11-29 00:56:26 -05:00
|
|
|
|
case CDIO_MMC_FEATURE_PROF_NON_CONFORM:
|
|
|
|
|
|
return "The Logical Unit does not conform to any Profile";
|
|
|
|
|
|
default:
|
|
|
|
|
|
{
|
|
|
|
|
|
static char buf[100];
|
|
|
|
|
|
snprintf(buf, sizeof(buf), "Unknown Profile %x", i_feature_profile);
|
|
|
|
|
|
return buf;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
See if CD-ROM has feature with value value
|
|
|
|
|
|
@return true if we have the feature and false if not.
|
|
|
|
|
|
*/
|
2008-11-29 00:56:26 -05:00
|
|
|
|
bool_3way_t
|
|
|
|
|
|
mmc_have_interface( CdIo_t *p_cdio, cdio_mmc_feature_interface_t e_interface )
|
|
|
|
|
|
{
|
|
|
|
|
|
int i_status; /* Result of MMC command */
|
|
|
|
|
|
uint8_t buf[500] = { 0, }; /* Place to hold returned data */
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }}; /* Command Descriptor Buffer */
|
|
|
|
|
|
|
|
|
|
|
|
if (!p_cdio || !p_cdio->op.run_mmc_cmd) return nope;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf));
|
|
|
|
|
|
|
|
|
|
|
|
cdb.field[1] = CDIO_MMC_GET_CONF_NAMED_FEATURE;
|
|
|
|
|
|
cdb.field[3] = CDIO_MMC_FEATURE_CORE;
|
|
|
|
|
|
|
|
|
|
|
|
i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf),
|
|
|
|
|
|
&buf);
|
|
|
|
|
|
if (DRIVER_OP_SUCCESS == i_status) {
|
|
|
|
|
|
uint8_t *p;
|
|
|
|
|
|
uint32_t i_data;
|
|
|
|
|
|
uint8_t *p_max = buf + 65530;
|
|
|
|
|
|
|
|
|
|
|
|
i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf);
|
|
|
|
|
|
/* set to first sense feature code, and then walk through the masks */
|
|
|
|
|
|
p = buf + 8;
|
|
|
|
|
|
while( (p < &(buf[i_data])) && (p < p_max) ) {
|
|
|
|
|
|
uint16_t i_feature;
|
|
|
|
|
|
uint8_t i_feature_additional = p[3];
|
|
|
|
|
|
|
|
|
|
|
|
i_feature = CDIO_MMC_GET_LEN16(p);
|
|
|
|
|
|
if (CDIO_MMC_FEATURE_CORE == i_feature) {
|
|
|
|
|
|
uint8_t *q = p+4;
|
|
|
|
|
|
uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q);
|
|
|
|
|
|
if (e_interface == i_interface_standard) return yep;
|
|
|
|
|
|
}
|
|
|
|
|
|
p += i_feature_additional + 4;
|
|
|
|
|
|
}
|
|
|
|
|
|
return nope;
|
|
|
|
|
|
} else
|
|
|
|
|
|
return dunno;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Read sectors using SCSI-MMC GPCMD_READ_CD.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_read_data_sectors ( CdIo_t *p_cdio, void *p_buf,
|
|
|
|
|
|
lsn_t i_lsn, uint16_t i_blocksize,
|
|
|
|
|
|
uint32_t i_blocks )
|
|
|
|
|
|
{
|
|
|
|
|
|
return mmc_read_cd(p_cdio,
|
|
|
|
|
|
p_buf, /* place to store data */
|
|
|
|
|
|
i_lsn, /* lsn */
|
|
|
|
|
|
0, /* read_sector_type */
|
|
|
|
|
|
false, /* digital audio play */
|
|
|
|
|
|
false, /* return sync header */
|
|
|
|
|
|
0, /* header codes */
|
|
|
|
|
|
true, /* return user data */
|
|
|
|
|
|
false, /* return EDC ECC */
|
|
|
|
|
|
false, /* return C2 Error information */
|
|
|
|
|
|
0, /* subchannel selection bits */
|
|
|
|
|
|
ISO_BLOCKSIZE, /* blocksize*/
|
|
|
|
|
|
i_blocks /* Number of blocks. */);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2010-01-28 05:06:15 -05:00
|
|
|
|
/**
|
|
|
|
|
|
Read sectors using SCSI-MMC GPCMD_READ_CD.
|
|
|
|
|
|
Can read only up to 25 blocks.
|
2008-11-29 00:56:26 -05:00
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_read_sectors ( const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn,
|
|
|
|
|
|
int sector_type, uint32_t i_blocks )
|
|
|
|
|
|
{
|
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
|
|
|
|
|
|
|
mmc_run_cmd_fn_t run_mmc_cmd;
|
|
|
|
|
|
|
|
|
|
|
|
if (!p_cdio) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
if (!p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED;
|
|
|
|
|
|
|
|
|
|
|
|
run_mmc_cmd = p_cdio->op.run_mmc_cmd;
|
|
|
|
|
|
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);
|
|
|
|
|
|
CDIO_MMC_SET_READ_TYPE (cdb.field, sector_type);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LBA (cdb.field, i_lsn);
|
|
|
|
|
|
CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks);
|
|
|
|
|
|
CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb.field,
|
|
|
|
|
|
CDIO_MMC_MCSB_ALL_HEADERS);
|
|
|
|
|
|
|
|
|
|
|
|
return run_mmc_cmd (p_cdio->env, mmc_timeout_ms,
|
|
|
|
|
|
mmc_get_cmd_len(cdb.field[0]), &cdb,
|
|
|
|
|
|
SCSI_MMC_DATA_READ,
|
|
|
|
|
|
CDIO_CD_FRAMESIZE_RAW * i_blocks,
|
|
|
|
|
|
p_buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_set_blocksize ( const CdIo_t *p_cdio, uint16_t i_blocksize)
|
|
|
|
|
|
{
|
|
|
|
|
|
if ( ! p_cdio ) return DRIVER_OP_UNINIT;
|
|
|
|
|
|
return
|
|
|
|
|
|
mmc_set_blocksize_private (p_cdio->env, p_cdio->op.run_mmc_cmd,
|
|
|
|
|
|
i_blocksize);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
Set the drive speed in CD-ROM speed units.
|
|
|
|
|
|
|
|
|
|
|
|
@param p_cdio CD structure set by cdio_open().
|
|
|
|
|
|
@param i_drive_speed speed in CD-ROM speed units. Note this
|
|
|
|
|
|
not Kbytes/sec as would be used in the MMC spec or
|
|
|
|
|
|
in mmc_set_speed(). To convert CD-ROM speed units
|
|
|
|
|
|
to Kbs, multiply the number by 176 (for raw data)
|
|
|
|
|
|
and by 150 (for filesystem data). On many CD-ROM
|
|
|
|
|
|
drives, specifying a value too large will result
|
|
|
|
|
|
in using the fastest speed.
|
|
|
|
|
|
|
|
|
|
|
|
@return the drive speed if greater than 0. -1 if we had an error. is -2
|
|
|
|
|
|
returned if this is not implemented for the current driver.
|
|
|
|
|
|
|
|
|
|
|
|
@see cdio_set_speed and mmc_set_speed
|
|
|
|
|
|
*/
|
|
|
|
|
|
driver_return_code_t
|
|
|
|
|
|
mmc_set_drive_speed( const CdIo_t *p_cdio, int i_drive_speed )
|
|
|
|
|
|
{
|
|
|
|
|
|
return mmc_set_speed(p_cdio, i_drive_speed * 176);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
|
Set the drive speed in K bytes per second.
|
|
|
|
|
|
|
|
|
|
|
|
@return the drive speed if greater than 0. -1 if we had an error. is -2
|
|
|
|
|
|
returned if this is not implemented for the current driver.
|
|
|
|
|
|
*/
|
|
|
|
|
|
int
|
|
|
|
|
|
mmc_set_speed( const CdIo_t *p_cdio, int i_Kbs_speed )
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
uint8_t buf[14] = { 0, };
|
|
|
|
|
|
mmc_cdb_t cdb;
|
|
|
|
|
|
|
|
|
|
|
|
/* If the requested speed is less than 1x 176 kb/s this command
|
|
|
|
|
|
will return an error - it's part of the ATAPI specs. Therefore,
|
|
|
|
|
|
test and stop early. */
|
|
|
|
|
|
|
|
|
|
|
|
if ( i_Kbs_speed < 176 ) return -1;
|
|
|
|
|
|
|
|
|
|
|
|
memset(&cdb, 0, sizeof(mmc_cdb_t));
|
|
|
|
|
|
CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_SET_SPEED);
|
|
|
|
|
|
CDIO_MMC_SET_LEN16(cdb.field, 2, i_Kbs_speed);
|
|
|
|
|
|
/* Some drives like the Creative 24x CDRW require one to set a
|
|
|
|
|
|
nonzero write speed or else one gets an error back. Some
|
|
|
|
|
|
specifications have setting the value 0xfffff indicate setting to
|
|
|
|
|
|
the maximum allowable speed.
|
|
|
|
|
|
*/
|
|
|
|
|
|
CDIO_MMC_SET_LEN16(cdb.field, 4, 0xffff);
|
|
|
|
|
|
return mmc_run_cmd(p_cdio, 2000, &cdb, SCSI_MMC_DATA_WRITE,
|
|
|
|
|
|
sizeof(buf), buf);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
* Local variables:
|
|
|
|
|
|
* c-file-style: "gnu"
|
|
|
|
|
|
* tab-width: 8
|
|
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
|
|
* End:
|
|
|
|
|
|
*/
|