Blame lib/driver/mmc/mmc.c

Packit dd8086
/* Common Multimedia Command (MMC) routines.
Packit dd8086
  Copyright (C) 2004-2008, 2010-2012, 2014
Packit dd8086
  Rocky Bernstein <rocky@gnu.org>
Packit dd8086
Packit dd8086
  This program is free software: you can redistribute it and/or modify
Packit dd8086
  it under the terms of the GNU General Public License as published by
Packit dd8086
  the Free Software Foundation, either version 3 of the License, or
Packit dd8086
  (at your option) any later version.
Packit dd8086
Packit dd8086
  This program is distributed in the hope that it will be useful,
Packit dd8086
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit dd8086
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit dd8086
  GNU General Public License for more details.
Packit dd8086
Packit dd8086
  You should have received a copy of the GNU General Public License
Packit dd8086
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit dd8086
*/
Packit dd8086
Packit dd8086
#ifdef HAVE_CONFIG_H
Packit dd8086
# include "config.h"
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDBOOL_H
Packit dd8086
# include <stdbool.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#include <cdio/cdio.h>
Packit dd8086
#include <cdio/logging.h>
Packit dd8086
#include <cdio/mmc.h>
Packit dd8086
#include <cdio/mmc_cmds.h>
Packit dd8086
#include <cdio/util.h>
Packit dd8086
#include "cdio_private.h"
Packit dd8086
#include "cdtext_private.h"
Packit dd8086
Packit dd8086
#ifdef HAVE_STRING_H
Packit dd8086
#include <string.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDLIB_H
Packit dd8086
#include <stdlib.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_STDIO_H
Packit dd8086
# include <stdio.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
#ifdef HAVE_ERRNO_H
Packit dd8086
# include <errno.h>
Packit dd8086
#endif
Packit dd8086
Packit dd8086
const char
Packit dd8086
*mmc_cmd2str(uint8_t command)
Packit dd8086
{
Packit dd8086
  switch( command ) {
Packit dd8086
  case CDIO_MMC_GPCMD_TEST_UNIT_READY:
Packit dd8086
    return "TEST UNIT READY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_REQUEST_SENSE:
Packit dd8086
    return "REQUEST SENSE";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_FORMAT_UNIT:
Packit dd8086
    return "FORMAT UNIT";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_INQUIRY:
Packit dd8086
    return "INQUIRY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_MODE_SELECT_6:
Packit dd8086
    return "MODE SELECT (6)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_MODE_SENSE_6:
Packit dd8086
    return "MODE SENSE (6)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_START_STOP_UNIT:
Packit dd8086
    return "START STOP UNIT";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
Packit dd8086
    return "PREVENT ALLOW MEDIUM REMOVAL";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_FORMAT_CAPACITIES:
Packit dd8086
    return "READ FORMAT CAPACITIES";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_CAPACITIY:
Packit dd8086
    return "READ_CAPACITIY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_10:
Packit dd8086
    return "READ (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_WRITE_10:
Packit dd8086
    return "WRITE (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SEEK_10:
Packit dd8086
    return "SEEK (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_ERASE_10:
Packit dd8086
    return "ERASE (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_WRITE_AND_VERIFY_10:
Packit dd8086
    return "WRITE AND VERIFY (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_VERIFY_10:
Packit dd8086
   return "VERIFY (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SYNCHRONIZE_CACHE:
Packit dd8086
    return "SYNCHRONIZE CACHE";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_WRITE_BUFFER:
Packit dd8086
    return "WRITE BUFFER";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_BUFFER:
Packit dd8086
    return "READ_BUFFER";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_SUBCHANNEL:
Packit dd8086
    return "READ_SUBCHANNEL";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_TOC:
Packit dd8086
    return "READ TOC";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_HEADER:
Packit dd8086
    return "READ_HEADER";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_AUDIO_10:
Packit dd8086
    return "PLAY AUDIO (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_GET_CONFIGURATION:
Packit dd8086
    return "GET_CONFIGURATION";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_AUDIO_MSF:
Packit dd8086
    return "PLAY AUDIO MSF";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_AUDIO_TI:
Packit dd8086
    return "PLAY_AUDIO TI";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_TRACK_REL_10:
Packit dd8086
    return "PLAY TRACK REL (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_GET_EVENT_STATUS:
Packit dd8086
    return "GET EVENT STATUS";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PAUSE_RESUME:
Packit dd8086
    return "PAUSE RESUME";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_DISC_INFORMATION:
Packit dd8086
    return "READ DISC INFORMATION";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_TRACK_INFORMATION:
Packit dd8086
    return "READ TRACK INFORMATION";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_RESERVE_TRACK:
Packit dd8086
    return "RESERVE TRACK";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SEND_OPC_INFORMATION:
Packit dd8086
    return "SEND OPC INFORMATION";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_MODE_SELECT_10:
Packit dd8086
    return "MODE SELECT (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_REPAIR_TRACK:
Packit dd8086
    return "REPAIR_TRACK";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_MODE_SENSE_10:
Packit dd8086
    return "MODE SENSE (10)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_CLOSE_TRACK_SESSION:
Packit dd8086
    return "CLOSE TRACK SESSION";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_BUFFER_CAPACITY:
Packit dd8086
    return "READ_BUFFER CAPACITY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SEND_CUE_SHEET:
Packit dd8086
    return "SEND_CUE SHEET";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_REPORT_LUNS:
Packit dd8086
    return "REPORT LUNS";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_BLANK:
Packit dd8086
    return "BLANK";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SECURITY_PROTOCOL_IN:
Packit dd8086
    return "SECURITY PROTOCOL IN";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SEND_KEY:
Packit dd8086
    return "SEND KEY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_REPORT_KEY:
Packit dd8086
    return "REPORT KEY";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_AUDIO_12:
Packit dd8086
    return "PLAY_AUDIO (12)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_LOAD_UNLOAD:
Packit dd8086
    return "LOAD UNLOAD";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SET_READ_AHEAD:
Packit dd8086
    return "SET READ AHEAD";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_12:
Packit dd8086
    return "READ (12)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAY_TRACK_REL_12:
Packit dd8086
    return "PLAY_TRACK REL (12)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_WRITE_12:
Packit dd8086
    return "WRITE (12)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_MEDIA_SERIAL_12:
Packit dd8086
    return "READ MEDIA SERIAL (12)";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_GET_PERFORMANCE:
Packit dd8086
    return "GET PERFORMANCE";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_DVD_STRUCTURE:
Packit dd8086
    return "READ DVD STRUCTURE";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SECURITY_PROTOCOL_OUT:
Packit dd8086
    return "SECURITY PROTOCOL_OUT";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SET_STREAMING:
Packit dd8086
    return "SET STREAMING";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_MSF:
Packit dd8086
    return "READ MSF";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SET_SPEED:
Packit dd8086
    return "SET SPEED";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_MECHANISM_STATUS:
Packit dd8086
    return "MECHANISM STATUS";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_CD:
Packit dd8086
    return "READ CD";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_SEND_DISC_STRUCTURE:
Packit dd8086
    return "SEND DISC STRUCTURE";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_CD_PLAYBACK_STATUS:
Packit dd8086
    return "CD PLAYBACK STATUS";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_PLAYBACK_CONTROL:
Packit dd8086
    return "PLAYBACK CONTROL";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_CDDA:
Packit dd8086
    return "READ CDDA";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_CDXA:
Packit dd8086
    return "READ CDXA";
Packit dd8086
Packit dd8086
  case CDIO_MMC_GPCMD_READ_ALL_SUBCODES:
Packit dd8086
    return "READ ALL SUBCODES";
Packit dd8086
Packit dd8086
  default:
Packit dd8086
    {
Packit dd8086
      char buf[30];
Packit dd8086
      snprintf(buf, sizeof(buf), "Unknown 0x%x", command);
Packit dd8086
      return strdup(buf);
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/*************************************************************************
Packit dd8086
  MMC CdIo Operations which a driver may use.
Packit dd8086
  These are not accessible directly.
Packit dd8086
Packit dd8086
  Most of these routines just pick out the cdio pointer and call the
Packit dd8086
  corresponding publically-accessible routine.
Packit dd8086
*************************************************************************/
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Read Audio Subchannel information
Packit dd8086
Packit dd8086
  @param p_user_data the CD object to be acted upon.
Packit dd8086
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
audio_read_subchannel_mmc ( void *p_user_data, cdio_subchannel_t *p_subchannel)
Packit dd8086
{
Packit dd8086
  generic_img_private_t *p_env = p_user_data;
Packit dd8086
  if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
  return mmc_audio_read_subchannel(p_env->cdio, p_subchannel);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get the block size for subsequest read requests, via MMC.
Packit dd8086
  @return the blocksize if > 0; error if <= 0
Packit dd8086
 */
Packit dd8086
int
Packit dd8086
get_blocksize_mmc (void *p_user_data)
Packit dd8086
{
Packit dd8086
    generic_img_private_t *p_env = p_user_data;
Packit dd8086
    if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
    return mmc_get_blocksize(p_env->cdio);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get the lsn of the end of the CD (via MMC).
Packit dd8086
*/
Packit dd8086
lsn_t
Packit dd8086
get_disc_last_lsn_mmc (void *p_user_data)
Packit dd8086
{
Packit dd8086
    generic_img_private_t *p_env = p_user_data;
Packit dd8086
    if (!p_env) return CDIO_INVALID_LSN;
Packit dd8086
    return mmc_get_disc_last_lsn(p_env->cdio);
Packit dd8086
}
Packit dd8086
Packit dd8086
void
Packit dd8086
get_drive_cap_mmc (const void *p_user_data,
Packit dd8086
		   /*out*/ cdio_drive_read_cap_t  *p_read_cap,
Packit dd8086
		   /*out*/ cdio_drive_write_cap_t *p_write_cap,
Packit dd8086
		   /*out*/ cdio_drive_misc_cap_t  *p_misc_cap)
Packit dd8086
{
Packit dd8086
  const generic_img_private_t *p_env = p_user_data;
Packit dd8086
  mmc_get_drive_cap( p_env->cdio,
Packit dd8086
                     p_read_cap, p_write_cap, p_misc_cap );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
    Find out if media has changed since the last call.  @param
Packit dd8086
    p_user_data the environment of the CD object to be acted upon.
Packit dd8086
    @return 1 if media has changed since last call, 0 if not. Error
Packit dd8086
    return codes are the same as driver_return_code_t
Packit dd8086
*/
Packit dd8086
int
Packit dd8086
get_media_changed_mmc (const void *p_user_data)
Packit dd8086
{
Packit dd8086
  const generic_img_private_t *p_env = p_user_data;
Packit dd8086
  return mmc_get_media_changed( p_env->cdio );
Packit dd8086
}
Packit dd8086
Packit dd8086
char *
Packit dd8086
get_mcn_mmc (const void *p_user_data)
Packit dd8086
{
Packit dd8086
  const generic_img_private_t *p_env = p_user_data;
Packit dd8086
  return mmc_get_mcn( p_env->cdio );
Packit dd8086
}
Packit dd8086
Packit dd8086
driver_return_code_t
Packit dd8086
get_tray_status (const void *p_user_data)
Packit dd8086
{
Packit dd8086
    const generic_img_private_t *p_env = p_user_data;
Packit dd8086
    return mmc_get_tray_status( p_env->cdio );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
    Read sectors using SCSI-MMC GPCMD_READ_CD.
Packit dd8086
 */
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_read_data_sectors ( CdIo_t *p_cdio, void *p_buf,
Packit dd8086
                        lsn_t i_lsn,  uint16_t i_blocksize,
Packit dd8086
                        uint32_t i_blocks )
Packit dd8086
{
Packit dd8086
  return mmc_read_cd(p_cdio,
Packit dd8086
                     p_buf, /* place to store data */
Packit dd8086
                     i_lsn, /* lsn */
Packit dd8086
                     0, /* read_sector_type */
Packit dd8086
                     false, /* digital audio play */
Packit dd8086
                     false, /* return sync header */
Packit dd8086
                     0,     /* header codes */
Packit dd8086
                     true,  /* return user data */
Packit dd8086
                     false, /* return EDC ECC */
Packit dd8086
                     false, /* return C2 Error information */
Packit dd8086
                     0,     /* subchannel selection bits */
Packit dd8086
                     ISO_BLOCKSIZE, /* blocksize*/
Packit dd8086
                     i_blocks       /* Number of blocks. */);
Packit dd8086
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Read sectors using SCSI-MMC GPCMD_READ_CD.
Packit dd8086
   Can read only up to 25 blocks.
Packit dd8086
 */
Packit dd8086
driver_return_code_t
Packit dd8086
read_data_sectors_mmc ( void *p_user_data, void *p_buf,
Packit dd8086
                        lsn_t i_lsn,  uint16_t i_blocksize,
Packit dd8086
                        uint32_t i_blocks )
Packit dd8086
{
Packit dd8086
    const generic_img_private_t *p_env = p_user_data;
Packit dd8086
    return mmc_read_data_sectors( p_env->cdio, p_buf, i_lsn, i_blocksize,
Packit dd8086
                                i_blocks );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
    Set read blocksize (via MMC)
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
set_blocksize_mmc (void *p_user_data, uint16_t i_blocksize)
Packit dd8086
{
Packit dd8086
    generic_img_private_t *p_env = p_user_data;
Packit dd8086
    if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
    return mmc_set_blocksize(p_env->cdio, i_blocksize);
Packit dd8086
}
Packit dd8086
Packit dd8086
/** Set the drive speed Set the drive speed in K bytes per second. (via
Packit dd8086
   MMC).
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
set_speed_mmc (void *p_user_data, int i_speed)
Packit dd8086
{
Packit dd8086
    generic_img_private_t *p_env = p_user_data;
Packit dd8086
    if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
    return mmc_set_speed( p_env->cdio, i_speed, 0);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Set the drive speed in CD-ROM speed units (via MMC).
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
set_drive_speed_mmc (void *p_user_data, int i_Kbs_speed)
Packit dd8086
{
Packit dd8086
  generic_img_private_t *p_env = p_user_data;
Packit dd8086
  if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
  return mmc_set_drive_speed( p_env->cdio, i_Kbs_speed );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
    Get the output port volumes and port selections used on AUDIO PLAY
Packit dd8086
    commands via a MMC MODE SENSE command using the CD Audio Control
Packit dd8086
    Page.
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_audio_get_volume( CdIo_t *p_cdio, /*out*/ mmc_audio_volume_t *p_volume )
Packit dd8086
{
Packit dd8086
  uint8_t buf[16];
Packit dd8086
  int i_rc = mmc_mode_sense(p_cdio, buf, sizeof(buf), CDIO_MMC_AUDIO_CTL_PAGE);
Packit dd8086
Packit dd8086
  if ( DRIVER_OP_SUCCESS == i_rc ) {
Packit dd8086
    p_volume->port[0].selection = 0xF & buf[8];
Packit dd8086
    p_volume->port[0].volume    = buf[9];
Packit dd8086
    p_volume->port[1].selection = 0xF & buf[10];
Packit dd8086
    p_volume->port[1].volume    = buf[11];
Packit dd8086
    p_volume->port[2].selection = 0xF & buf[12];
Packit dd8086
    p_volume->port[2].volume    = buf[13];
Packit dd8086
    p_volume->port[3].selection = 0xF & buf[14];
Packit dd8086
    p_volume->port[3].volume    = buf[15];
Packit dd8086
    return DRIVER_OP_SUCCESS;
Packit dd8086
  }
Packit dd8086
  return i_rc;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get the DVD type associated with cd object.
Packit dd8086
*/
Packit dd8086
discmode_t
Packit dd8086
mmc_get_dvd_struct_physical_private ( void *p_env,
Packit dd8086
                                      mmc_run_cmd_fn_t run_mmc_cmd,
Packit dd8086
                                      cdio_dvd_struct_t *s)
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb = {{0, }};
Packit dd8086
  unsigned char buf[4 + 4 * 20], *base;
Packit dd8086
  int i_status;
Packit dd8086
  uint8_t layer_num = s->physical.layer_num;
Packit dd8086
Packit dd8086
  cdio_dvd_layer_t *layer;
Packit dd8086
Packit dd8086
  if (!p_env) return DRIVER_OP_UNINIT;
Packit dd8086
  if (!run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
Packit dd8086
  if (layer_num >= CDIO_DVD_MAX_LAYERS)
Packit dd8086
    return -EINVAL;
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_DVD_STRUCTURE);
Packit dd8086
  cdb.field[6] = layer_num;
Packit dd8086
  cdb.field[7] = CDIO_DVD_STRUCT_PHYSICAL;
Packit dd8086
  cdb.field[9] = sizeof(buf) & 0xff;
Packit dd8086
Packit dd8086
  i_status = run_mmc_cmd(p_env, mmc_timeout_ms,
Packit dd8086
			      mmc_get_cmd_len(cdb.field[0]),
Packit dd8086
			      &cdb, SCSI_MMC_DATA_READ,
Packit dd8086
			      sizeof(buf), &buf;;
Packit dd8086
  if (0 != i_status)
Packit dd8086
    return CDIO_DISC_MODE_ERROR;
Packit dd8086
Packit dd8086
  base = &buf[4];
Packit dd8086
  layer = &s->physical.layer[layer_num];
Packit dd8086
Packit dd8086
  /*
Packit dd8086
   * place the data... really ugly, but at least we won't have to
Packit dd8086
   * worry about endianess in userspace.
Packit dd8086
   */
Packit dd8086
  memset(layer, 0, sizeof(*layer));
Packit dd8086
  layer->book_version = base[0] & 0xf;
Packit dd8086
  layer->book_type = base[0] >> 4;
Packit dd8086
  layer->min_rate = base[1] & 0xf;
Packit dd8086
  layer->disc_size = base[1] >> 4;
Packit dd8086
  layer->layer_type = base[2] & 0xf;
Packit dd8086
  layer->track_path = (base[2] >> 4) & 1;
Packit dd8086
  layer->nlayers = (base[2] >> 5) & 3;
Packit dd8086
  layer->track_density = base[3] & 0xf;
Packit dd8086
  layer->linear_density = base[3] >> 4;
Packit dd8086
  layer->start_sector = base[5] << 16 | base[6] << 8 | base[7];
Packit dd8086
  layer->end_sector = base[9] << 16 | base[10] << 8 | base[11];
Packit dd8086
  layer->end_sector_l0 = base[13] << 16 | base[14] << 8 | base[15];
Packit dd8086
  layer->bca = base[16] >> 7;
Packit dd8086
Packit dd8086
  return (discmode_t) DRIVER_OP_SUCCESS;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get the media catalog number (MCN) or the ISRC
Packit dd8086
Packit dd8086
  Note: string is malloc'd so caller should free() then returned
Packit dd8086
  string when done with it.
Packit dd8086
Packit dd8086
  @param p_cdio
Packit dd8086
  @param i_track track number
Packit dd8086
  @param sub_chan_param 2 for MCN, 3 for ISRC
Packit dd8086
Packit dd8086
  @return malloc'd string holding the MCN or ISRC on success
Packit dd8086
          or NULL on failure
Packit dd8086
 */
Packit dd8086
char *
Packit dd8086
mmc_get_mcn_isrc_private ( const CdIo_t *p_cdio,
Packit dd8086
                            track_t i_track,
Packit dd8086
                            unsigned char sub_chan_param
Packit dd8086
                  )
Packit dd8086
{
Packit dd8086
  char buf[24]; /* 4 header + 20 data (MMC-4 tables 424, 431, 432) */
Packit dd8086
  unsigned int num_data;
Packit dd8086
  size_t length;
Packit dd8086
  driver_return_code_t i_rc;
Packit dd8086
Packit dd8086
  switch(sub_chan_param) {
Packit dd8086
    case CDIO_SUBCHANNEL_MEDIA_CATALOG: /* MCN */
Packit dd8086
      length = CDIO_MCN_SIZE;
Packit dd8086
      break;
Packit dd8086
    case CDIO_SUBCHANNEL_TRACK_ISRC: /* ISRC */
Packit dd8086
      length = CDIO_ISRC_SIZE;
Packit dd8086
      break;
Packit dd8086
    default:
Packit dd8086
      return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* inquire number of available reply bytes
Packit dd8086
     workaround for bad device drivers
Packit dd8086
   */
Packit dd8086
  num_data = 4; /* header only */
Packit dd8086
  i_rc = mmc_read_subchannel (p_cdio, i_track,
Packit dd8086
                                   sub_chan_param, &num_data, buf, 0);
Packit dd8086
Packit dd8086
  if (i_rc != DRIVER_OP_SUCCESS)
Packit dd8086
    return NULL;
Packit dd8086
Packit dd8086
  if (num_data > sizeof(buf))
Packit dd8086
    num_data = sizeof(buf);
Packit dd8086
Packit dd8086
  if (num_data < 9 + length)
Packit dd8086
    return NULL;              /* Not enough data available */
Packit dd8086
Packit dd8086
  i_rc = mmc_read_subchannel (p_cdio, i_track,
Packit dd8086
                                   sub_chan_param, &num_data, buf, 0);
Packit dd8086
  if (i_rc != DRIVER_OP_SUCCESS)
Packit dd8086
    return NULL;
Packit dd8086
Packit dd8086
  if (num_data < 9 + length)
Packit dd8086
    return NULL;              /* Not enough data returned */
Packit dd8086
Packit dd8086
  if ( ! (buf[8] & 0x80) )    /* MCVAL / TCVAL bit indicates a valid response */
Packit dd8086
    return NULL;              /* MCN/ISRC not valid */
Packit dd8086
  return strndup(&buf[9], length);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_set_blocksize_private ( void *p_env,
Packit dd8086
                            const mmc_run_cmd_fn_t run_mmc_cmd,
Packit dd8086
                            uint16_t i_blocksize)
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb = {{0, }};
Packit dd8086
Packit dd8086
  struct
Packit dd8086
  {
Packit dd8086
    uint8_t reserved1;
Packit dd8086
    uint8_t medium;
Packit dd8086
    uint8_t reserved2;
Packit dd8086
    uint8_t block_desc_length;
Packit dd8086
    uint8_t density;
Packit dd8086
    uint8_t number_of_blocks_hi;
Packit dd8086
    uint8_t number_of_blocks_med;
Packit dd8086
    uint8_t number_of_blocks_lo;
Packit dd8086
    uint8_t reserved3;
Packit dd8086
    uint8_t block_length_hi;
Packit dd8086
    uint8_t block_length_med;
Packit dd8086
    uint8_t block_length_lo;
Packit dd8086
  } mh;
Packit dd8086
Packit dd8086
  if ( ! p_env ) return DRIVER_OP_UNINIT;
Packit dd8086
  if ( ! run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
Packit dd8086
  memset (&mh, 0, sizeof (mh));
Packit dd8086
  mh.block_desc_length = 0x08;
Packit dd8086
Packit dd8086
  /* while i_blocksize is uint16_t, this expression is always 0 */
Packit dd8086
  mh.block_length_hi   = (i_blocksize >> 16) & 0xff;
Packit dd8086
Packit dd8086
  mh.block_length_med  = (i_blocksize >>  8) & 0xff;
Packit dd8086
  mh.block_length_lo   = (i_blocksize >>  0) & 0xff;
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SELECT_6);
Packit dd8086
Packit dd8086
  cdb.field[1] = 1 << 4;
Packit dd8086
  cdb.field[4] = 12;
Packit dd8086
Packit dd8086
  return run_mmc_cmd (p_env, mmc_timeout_ms,
Packit dd8086
			      mmc_get_cmd_len(cdb.field[0]), &cdb,
Packit dd8086
			      SCSI_MMC_DATA_WRITE, sizeof(mh), &mh);
Packit dd8086
}
Packit dd8086
Packit dd8086
Packit dd8086
Packit dd8086
/***********************************************************
Packit dd8086
  User-accessible Operations.
Packit dd8086
************************************************************/
Packit dd8086
/**
Packit dd8086
  Read Audio Subchannel information
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_audio_read_subchannel (CdIo_t *p_cdio,  cdio_subchannel_t *p_subchannel)
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb;
Packit dd8086
  driver_return_code_t i_rc;
Packit dd8086
  cdio_mmc_subchannel_t mmc_subchannel;
Packit dd8086
Packit dd8086
  if (!p_cdio) return DRIVER_OP_UNINIT;
Packit dd8086
Packit dd8086
  memset(&mmc_subchannel, 0, sizeof(mmc_subchannel));
Packit dd8086
  mmc_subchannel.format = CDIO_CDROM_MSF;
Packit dd8086
  memset(&cdb, 0, sizeof(mmc_cdb_t));
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_SUBCHANNEL);
Packit dd8086
  CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(cdio_mmc_subchannel_t));
Packit dd8086
Packit dd8086
  cdb.field[1] = CDIO_CDROM_MSF;
Packit dd8086
  cdb.field[2] = 0x40; /* subq */
Packit dd8086
  cdb.field[3] = CDIO_SUBCHANNEL_CURRENT_POSITION;
Packit dd8086
  cdb.field[6] = 0;    /* track number (only in isrc mode, ignored) */
Packit dd8086
Packit dd8086
  i_rc = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ,
Packit dd8086
                     sizeof(cdio_mmc_subchannel_t), &mmc_subchannel);
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_rc) {
Packit dd8086
    p_subchannel->format       = mmc_subchannel.format;
Packit dd8086
    p_subchannel->audio_status = mmc_subchannel.audio_status;
Packit dd8086
    p_subchannel->address      = mmc_subchannel.address;
Packit dd8086
    p_subchannel->control      = mmc_subchannel.control;
Packit dd8086
    p_subchannel->track        = mmc_subchannel.track;
Packit dd8086
    p_subchannel->index        = mmc_subchannel.index;
Packit dd8086
    p_subchannel->abs_addr.m   = cdio_to_bcd8(mmc_subchannel.abs_addr[1]);
Packit dd8086
    p_subchannel->abs_addr.s   = cdio_to_bcd8(mmc_subchannel.abs_addr[2]);
Packit dd8086
    p_subchannel->abs_addr.f   = cdio_to_bcd8(mmc_subchannel.abs_addr[3]);
Packit dd8086
    p_subchannel->rel_addr.m   = cdio_to_bcd8(mmc_subchannel.rel_addr[1]);
Packit dd8086
    p_subchannel->rel_addr.s   = cdio_to_bcd8(mmc_subchannel.rel_addr[2]);
Packit dd8086
    p_subchannel->rel_addr.f   = cdio_to_bcd8(mmc_subchannel.rel_addr[3]);
Packit dd8086
  }
Packit dd8086
  return i_rc;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get the block size used in read requests, via MMC (e.g. READ_10,
Packit dd8086
   READ_MSF, ...)
Packit dd8086
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @return the blocksize if > 0; error if <= 0
Packit dd8086
*/
Packit dd8086
int
Packit dd8086
mmc_get_blocksize ( CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  int i_status;
Packit dd8086
Packit dd8086
  uint8_t buf[255] = { 0, };
Packit dd8086
  uint8_t *p;
Packit dd8086
Packit dd8086
  /* First try using the 6-byte MODE SENSE command. */
Packit dd8086
  i_status = mmc_mode_sense_6(p_cdio, buf, sizeof(buf),
Packit dd8086
                              CDIO_MMC_R_W_ERROR_PAGE);
Packit dd8086
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_status && buf[3]>=8) {
Packit dd8086
    p = &buf[4+5];
Packit dd8086
    return CDIO_MMC_GET_LEN16(p);
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Next try using the 10-byte MODE SENSE command. */
Packit dd8086
  i_status = mmc_mode_sense_10(p_cdio, buf, sizeof(buf),
Packit dd8086
                               CDIO_MMC_R_W_ERROR_PAGE);
Packit dd8086
  p = &buf[6];
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_status && CDIO_MMC_GET_LEN16(p)>=8) {
Packit dd8086
    return CDIO_MMC_GET_LEN16(p);
Packit dd8086
  }
Packit dd8086
Packit dd8086
#ifdef IS_THIS_CORRECT
Packit dd8086
  /* Lastly try using the READ CAPACITY command. */
Packit dd8086
  {
Packit dd8086
    lba_t    lba = 0;
Packit dd8086
    uint16_t i_blocksize;
Packit dd8086
Packit dd8086
    i_status = mmc_read_capacity(p_cdio, &lba, &i_blocksize);
Packit dd8086
    if ( DRIVER_OP_SUCCESS == i_status )
Packit dd8086
      return i_blocksize;
Packit dd8086
#endif
Packit dd8086
Packit dd8086
  return DRIVER_OP_UNSUPPORTED;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the number of length in bytes of the Command Descriptor
Packit dd8086
  buffer (CDB) for a given MMC command. The length will be
Packit dd8086
  either 6, 10, or 12.
Packit dd8086
*/
Packit dd8086
uint8_t
Packit dd8086
mmc_get_cmd_len(uint8_t scsi_cmd)
Packit dd8086
{
Packit dd8086
  static const uint8_t scsi_cdblen[8] = {6, 10, 10, 12, 12, 12, 10, 10};
Packit dd8086
  return scsi_cdblen[((scsi_cmd >> 5) & 7)];
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Return the size of the CD in logical block address (LBA) units.
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @return the lsn. On error 0 or CDIO_INVALD_LSN.
Packit dd8086
 */
Packit dd8086
lsn_t
Packit dd8086
mmc_get_disc_last_lsn ( const CdIo_t *p_cdio )
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb = {{0, }};
Packit dd8086
  uint8_t buf[12] = { 0, };
Packit dd8086
Packit dd8086
  lsn_t retval = 0;
Packit dd8086
  int i_status;
Packit dd8086
Packit dd8086
  /* Operation code */
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
Packit dd8086
Packit dd8086
  cdb.field[1] = 0; /* lba; msf: 0x2 */
Packit dd8086
Packit dd8086
  /* Format */
Packit dd8086
  cdb.field[2] = CDIO_MMC_READTOC_FMT_TOC;
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_START_TRACK(cdb.field, CDIO_CDROM_LEADOUT_TRACK);
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf));
Packit dd8086
Packit dd8086
  i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms, &cdb, SCSI_MMC_DATA_READ,
Packit dd8086
                         sizeof(buf), buf);
Packit dd8086
Packit dd8086
  if (i_status) return CDIO_INVALID_LSN;
Packit dd8086
Packit dd8086
  {
Packit dd8086
    int i;
Packit dd8086
    for (i = 8; i < 12; i++) {
Packit dd8086
      retval <<= 8;
Packit dd8086
      retval += buf[i];
Packit dd8086
    }
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return retval;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Return the discmode as reported by the SCSI-MMC Read (FULL) TOC
Packit dd8086
  command.
Packit dd8086
Packit dd8086
  Information was obtained from Section 5.1.13 (Read TOC/PMA/ATIP)
Packit dd8086
  pages 56-62 from the MMC draft specification, revision 10a
Packit dd8086
  at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See
Packit dd8086
  especially tables 72, 73 and 75.
Packit dd8086
*/
Packit dd8086
discmode_t
Packit dd8086
mmc_get_discmode( const CdIo_t *p_cdio )
Packit dd8086
Packit dd8086
{
Packit dd8086
  uint8_t buf[14] = { 0, };
Packit dd8086
  mmc_cdb_t cdb;
Packit dd8086
Packit dd8086
  memset(&cdb, 0, sizeof(mmc_cdb_t));
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC);
Packit dd8086
  CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf));
Packit dd8086
Packit dd8086
  cdb.field[1] = CDIO_CDROM_MSF; /* The MMC-5 spec may require this. */
Packit dd8086
  cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC;
Packit dd8086
Packit dd8086
  mmc_run_cmd(p_cdio, 2000, &cdb, SCSI_MMC_DATA_READ, sizeof(buf), buf);
Packit dd8086
  if (buf[7] == 0xA0) {
Packit dd8086
    if (buf[13] == 0x00) {
Packit dd8086
      if (buf[5] & 0x04)
Packit dd8086
	return CDIO_DISC_MODE_CD_DATA;
Packit dd8086
      else
Packit dd8086
	return CDIO_DISC_MODE_CD_DA;
Packit dd8086
    }
Packit dd8086
    else if (buf[13] == 0x10)
Packit dd8086
      return CDIO_DISC_MODE_CD_I;
Packit dd8086
    else if (buf[13] == 0x20)
Packit dd8086
    return CDIO_DISC_MODE_CD_XA;
Packit dd8086
  }
Packit dd8086
  return CDIO_DISC_MODE_NO_INFO;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get drive capabilities for a device.
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @return the drive capabilities.
Packit dd8086
*/
Packit dd8086
void
Packit dd8086
mmc_get_drive_cap (CdIo_t *p_cdio,
Packit dd8086
                   /*out*/ cdio_drive_read_cap_t  *p_read_cap,
Packit dd8086
                   /*out*/ cdio_drive_write_cap_t *p_write_cap,
Packit dd8086
                   /*out*/ cdio_drive_misc_cap_t  *p_misc_cap)
Packit dd8086
{
Packit dd8086
  /* Largest buffer size we use. */
Packit dd8086
#define BUF_MAX 2048
Packit dd8086
  uint8_t buf[BUF_MAX+2] = { 0, };
Packit dd8086
Packit dd8086
  int i_status;
Packit dd8086
  uint16_t i_data = BUF_MAX;
Packit dd8086
  int page = CDIO_MMC_ALL_PAGES;
Packit dd8086
Packit dd8086
  if ( ! p_cdio )  return;
Packit dd8086
 retry:
Packit dd8086
Packit dd8086
  /* In the first run we run MODE SENSE 10 we are trying to get the
Packit dd8086
     length of the data features. */
Packit dd8086
  i_status = mmc_mode_sense_10(p_cdio, buf, 8, CDIO_MMC_ALL_PAGES);
Packit dd8086
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_status) {
Packit dd8086
    uint16_t i_data_try = (uint16_t) CDIO_MMC_GET_LEN16(buf);
Packit dd8086
    if (i_data_try < BUF_MAX) i_data = i_data_try;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  /* Now try getting all features with length set above, possibly
Packit dd8086
     truncated or the default length if we couldn't get the proper
Packit dd8086
     length. */
Packit dd8086
  i_status = mmc_mode_sense_10(p_cdio, buf, i_data, CDIO_MMC_ALL_PAGES);
Packit dd8086
  if (0 != i_status && CDIO_MMC_CAPABILITIES_PAGE != page) {
Packit dd8086
    page =  CDIO_MMC_CAPABILITIES_PAGE;
Packit dd8086
    goto retry;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_status) {
Packit dd8086
    uint8_t *p;
Packit dd8086
    uint8_t *p_max = buf + 256;
Packit dd8086
Packit dd8086
    *p_read_cap  = 0;
Packit dd8086
    *p_write_cap = 0;
Packit dd8086
    *p_misc_cap  = 0;
Packit dd8086
Packit dd8086
    /* set to first sense mask, and then walk through the masks */
Packit dd8086
    p = buf + 8;
Packit dd8086
    while( (p < &(buf[2+i_data])) && (p < p_max) )       {
Packit dd8086
      uint8_t which_page;
Packit dd8086
Packit dd8086
      which_page = p[0] & 0x3F;
Packit dd8086
      switch( which_page )
Packit dd8086
	{
Packit dd8086
	case CDIO_MMC_AUDIO_CTL_PAGE:
Packit dd8086
	case CDIO_MMC_R_W_ERROR_PAGE:
Packit dd8086
	case CDIO_MMC_CDR_PARMS_PAGE:
Packit dd8086
	  /* Don't handle these yet. */
Packit dd8086
	  break;
Packit dd8086
	case CDIO_MMC_CAPABILITIES_PAGE:
Packit dd8086
	  mmc_get_drive_cap_buf(p, p_read_cap, p_write_cap, p_misc_cap);
Packit dd8086
	  break;
Packit dd8086
	default: ;
Packit dd8086
	}
Packit dd8086
      p += (p[1] + 2);
Packit dd8086
    }
Packit dd8086
  } else {
Packit dd8086
    cdio_info("%s: %s\n", "error in MODE_SELECT", strerror(errno));
Packit dd8086
    *p_read_cap  = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
    *p_write_cap = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
    *p_misc_cap  = CDIO_DRIVE_CAP_ERROR;
Packit dd8086
  }
Packit dd8086
  return;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get the MMC level supported by the device.
Packit dd8086
*/
Packit dd8086
cdio_mmc_level_t
Packit dd8086
mmc_get_drive_mmc_cap(CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  uint8_t buf[256] = { 0, };
Packit dd8086
  uint8_t len;
Packit dd8086
  int rc = mmc_mode_sense(p_cdio, buf, sizeof(buf),
Packit dd8086
			  CDIO_MMC_CAPABILITIES_PAGE);
Packit dd8086
Packit dd8086
  if (DRIVER_OP_SUCCESS != rc) {
Packit dd8086
    return CDIO_MMC_LEVEL_NONE;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  len = buf[1];
Packit dd8086
  if (16 > len) {
Packit dd8086
    return CDIO_MMC_LEVEL_WEIRD;
Packit dd8086
  } else if (28 <= len) {
Packit dd8086
    return CDIO_MMC_LEVEL_3;
Packit dd8086
  } else if (24 <= len) {
Packit dd8086
    return CDIO_MMC_LEVEL_2;
Packit dd8086
  } else if (20 <= len) {
Packit dd8086
    return CDIO_MMC_LEVEL_1;
Packit dd8086
  } else {
Packit dd8086
    return CDIO_MMC_LEVEL_WEIRD;
Packit dd8086
  }
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get the DVD type associated with cd object.
Packit dd8086
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @return the DVD discmode.
Packit dd8086
*/
Packit dd8086
discmode_t
Packit dd8086
mmc_get_dvd_struct_physical ( const CdIo_t *p_cdio, cdio_dvd_struct_t *s)
Packit dd8086
{
Packit dd8086
  if ( ! p_cdio )  return -2;
Packit dd8086
  return
Packit dd8086
    mmc_get_dvd_struct_physical_private (p_cdio->env,
Packit dd8086
                                         p_cdio->op.run_mmc_cmd,
Packit dd8086
                                         s);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Get the CD-ROM hardware info via a MMC INQUIRY command.
Packit dd8086
  False is returned if we had an error getting the information.
Packit dd8086
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
  @return true if we were able to get hardware info, false if we had
Packit dd8086
  an error.
Packit dd8086
*/
Packit dd8086
bool
Packit dd8086
mmc_get_hwinfo ( const CdIo_t *p_cdio,
Packit dd8086
		      /*out*/ cdio_hwinfo_t *hw_info )
Packit dd8086
{
Packit dd8086
  int i_status;                  /* Result of MMC command */
Packit dd8086
  char buf[36] = { 0, };         /* Place to hold returned data */
Packit dd8086
  mmc_cdb_t cdb = {{0, }};  /* Command Descriptor Block */
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_INQUIRY);
Packit dd8086
  cdb.field[4] = sizeof(buf);
Packit dd8086
Packit dd8086
  if (! p_cdio || ! hw_info ) return false;
Packit dd8086
Packit dd8086
  i_status = mmc_run_cmd(p_cdio, mmc_timeout_ms,
Packit dd8086
			      &cdb, SCSI_MMC_DATA_READ,
Packit dd8086
			      sizeof(buf), &buf;;
Packit dd8086
  if (i_status == 0) {
Packit dd8086
Packit dd8086
      memcpy(hw_info->psz_vendor,
Packit dd8086
	     buf + 8,
Packit dd8086
	     sizeof(hw_info->psz_vendor)-1);
Packit dd8086
      hw_info->psz_vendor[sizeof(hw_info->psz_vendor)-1] = '\0';
Packit dd8086
      memcpy(hw_info->psz_model,
Packit dd8086
	     buf + 8 + CDIO_MMC_HW_VENDOR_LEN,
Packit dd8086
	     sizeof(hw_info->psz_model)-1);
Packit dd8086
      hw_info->psz_model[sizeof(hw_info->psz_model)-1] = '\0';
Packit dd8086
      memcpy(hw_info->psz_revision,
Packit dd8086
	     buf + 8 + CDIO_MMC_HW_VENDOR_LEN + CDIO_MMC_HW_MODEL_LEN,
Packit dd8086
	     sizeof(hw_info->psz_revision)-1);
Packit dd8086
      hw_info->psz_revision[sizeof(hw_info->psz_revision)-1] = '\0';
Packit dd8086
      return true;
Packit dd8086
    }
Packit dd8086
  return false;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Find out if media has changed since the last call.
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
  @return 1 if media has changed since last call, 0 if not. Error
Packit dd8086
  return codes are the same as driver_return_code_t
Packit dd8086
*/
Packit dd8086
int mmc_get_media_changed(const CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  uint8_t status_buf[2];
Packit dd8086
  int i_status;
Packit dd8086
Packit dd8086
  i_status = mmc_get_event_status(p_cdio, status_buf);
Packit dd8086
  if (i_status != DRIVER_OP_SUCCESS)
Packit dd8086
    return i_status;
Packit dd8086
  return (status_buf[0] & 0x02) ? 1 : 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get the media catalog number (MCN) from the CD via MMC.
Packit dd8086
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @return the media catalog number r NULL if there is none or we
Packit dd8086
   don't have the ability to get it.
Packit dd8086
Packit dd8086
   Note: The caller must free the returned string with cdio_free()
Packit dd8086
   when done with it.
Packit dd8086
Packit dd8086
*/
Packit dd8086
char *
Packit dd8086
mmc_get_mcn ( const CdIo_t *p_cdio )
Packit dd8086
{
Packit dd8086
  if ( ! p_cdio )  return NULL;
Packit dd8086
  return mmc_get_mcn_isrc_private (p_cdio, 0, CDIO_SUBCHANNEL_MEDIA_CATALOG );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
   Get the international standard recording code (ISRC) of the track via MMC.
Packit dd8086
   @param p_cdio the CD object to be acted upon.
Packit dd8086
   @param i_track the track to get the ISRC info for.
Packit dd8086
   @return the international standard recording code or NULL if there is
Packit dd8086
   none or we don't have the ability to get it.
Packit dd8086
Packit dd8086
   Note: The caller must free the returned string with cdio_free()
Packit dd8086
   when done with it.
Packit dd8086
Packit dd8086
*/
Packit dd8086
char *
Packit dd8086
mmc_get_track_isrc ( const CdIo_t *p_cdio, track_t i_track )
Packit dd8086
{
Packit dd8086
  if ( ! p_cdio )  return NULL;
Packit dd8086
  return mmc_get_mcn_isrc_private (p_cdio, i_track, CDIO_SUBCHANNEL_TRACK_ISRC );
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Read cdtext information for a CdIo_t object .
Packit dd8086
Packit dd8086
  @return pointer to data on success, NULL on error or CD-Text information does
Packit dd8086
  not exist.
Packit dd8086
Packit dd8086
  Note: the caller must free the returned memory
Packit dd8086
Packit dd8086
*/
Packit dd8086
uint8_t *
Packit dd8086
mmc_read_cdtext (const CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
Packit dd8086
  unsigned char buf[4];
Packit dd8086
  unsigned char * wdata;
Packit dd8086
  int           i_status;
Packit dd8086
  unsigned int  i_cdtext;
Packit dd8086
Packit dd8086
  if ( ! p_cdio )
Packit dd8086
    return NULL;
Packit dd8086
Packit dd8086
  /* We may need to give CD-Text a little more time to complete. */
Packit dd8086
  /* First off, just try and read the size */
Packit dd8086
  i_cdtext = 4;
Packit dd8086
  i_status = mmc_read_toc_cdtext(p_cdio, &i_cdtext, buf, 0);
Packit dd8086
Packit dd8086
  if (i_status != DRIVER_OP_SUCCESS) {
Packit dd8086
    return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  if (i_cdtext > CDTEXT_LEN_BINARY_MAX + 2)
Packit dd8086
      i_cdtext = CDTEXT_LEN_BINARY_MAX + 4;
Packit dd8086
  else
Packit dd8086
      i_cdtext += 2; /* data length does not include the data length field */
Packit dd8086
Packit dd8086
  wdata = malloc(i_cdtext); /* is zeroed in mmc_toc_read_cdtext */
Packit dd8086
Packit dd8086
  /* Read all of it */
Packit dd8086
  i_status = mmc_read_toc_cdtext(p_cdio, &i_cdtext, wdata, 0);
Packit dd8086
Packit dd8086
  if (i_status != DRIVER_OP_SUCCESS) {
Packit dd8086
      free(wdata);
Packit dd8086
      return NULL;
Packit dd8086
  }
Packit dd8086
Packit dd8086
  return wdata;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Find out if media tray is open or closed.
Packit dd8086
  @param p_cdio the CD object to be acted upon.
Packit dd8086
  @return 1 if media is open, 0 if closed. Error
Packit dd8086
  return codes are the same as driver_return_code_t
Packit dd8086
*/
Packit dd8086
int mmc_get_tray_status(const CdIo_t *p_cdio)
Packit dd8086
{
Packit dd8086
  uint8_t status_buf[2];
Packit dd8086
  int i_status;
Packit dd8086
Packit dd8086
  i_status = mmc_get_event_status(p_cdio, status_buf);
Packit dd8086
  if (i_status != DRIVER_OP_SUCCESS)
Packit dd8086
    return i_status;
Packit dd8086
  return (status_buf[1] & 0x01) ? 1 : 0;
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Added in version 0.83 by scdbackup */
Packit dd8086
/**
Packit dd8086
   Obtain the SCSI sense reply of the most-recently-performed MMC command.
Packit dd8086
   These bytes give an indication of possible problems which occured in
Packit dd8086
   the drive while the command was performed. With some commands they tell
Packit dd8086
   about the current state of the drive (e.g. 00h TEST UNIT READY).
Packit dd8086
   @param p_cdio CD structure set by cdio_open().
Packit dd8086
Packit dd8086
   @param sense returns the sense bytes received from the drive.
Packit dd8086
   This is allocated memory or NULL if no sense bytes are
Packit dd8086
   available. Dispose non-NULL pointers by cdio_free() when no longer
Packit dd8086
   needed.  See SPC-3 4.5.3 Fixed format sense data.  SCSI error
Packit dd8086
   codes as of SPC-3 Annex D, MMC-5 Annex F: sense[2]&15 = Key ,
Packit dd8086
   sense[12] = ASC , sense[13] = ASCQ
Packit dd8086
Packit dd8086
   @return number of valid bytes in sense, 0 in case of no sense
Packit dd8086
   bytes available, <0 in case of internal error.
Packit dd8086
  */
Packit dd8086
int
Packit dd8086
mmc_last_cmd_sense(const CdIo_t *p_cdio, cdio_mmc_request_sense_t **pp_sense)
Packit dd8086
{
Packit dd8086
    generic_img_private_t *gen;
Packit dd8086
Packit dd8086
    if (!p_cdio) return DRIVER_OP_UNINIT;
Packit dd8086
    gen = p_cdio->env;
Packit dd8086
    *pp_sense = NULL;
Packit dd8086
    if (gen->scsi_mmc_sense_valid <= 0)
Packit dd8086
	return 0;
Packit dd8086
    *pp_sense = calloc(1, gen->scsi_mmc_sense_valid);
Packit dd8086
    if (*pp_sense == NULL)
Packit dd8086
        return DRIVER_OP_ERROR;
Packit dd8086
    memcpy(*pp_sense, gen->scsi_mmc_sense, gen->scsi_mmc_sense_valid);
Packit dd8086
    return gen->scsi_mmc_sense_valid;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  Run a MMC command.
Packit dd8086
Packit dd8086
  @param cdio	       CD structure set by cdio_open().
Packit dd8086
  @param i_timeout     time in milliseconds we will wait for the command
Packit dd8086
                       to complete. If this value is -1, use the default
Packit dd8086
                       time-out value.
Packit dd8086
  @param buf	       Buffer for data, both sending and receiving
Packit dd8086
  @param len	       Size of buffer
Packit dd8086
  @param e_direction   direction the transfer is to go
Packit dd8086
  @param cdb	       CDB bytes. All values that are needed should be set on
Packit dd8086
                       input. We'll figure out what the right CDB length
Packit dd8086
                       should be.
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_run_cmd( const CdIo_t *p_cdio, unsigned int i_timeout_ms,
Packit dd8086
             const mmc_cdb_t *p_cdb,
Packit dd8086
             cdio_mmc_direction_t e_direction, unsigned int i_buf,
Packit dd8086
             /*in/out*/ void *p_buf )
Packit dd8086
{
Packit dd8086
    if (!p_cdio) return DRIVER_OP_UNINIT;
Packit dd8086
    if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
    return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms,
Packit dd8086
                                  mmc_get_cmd_len(p_cdb->field[0]),
Packit dd8086
                                  p_cdb, e_direction, i_buf, p_buf);
Packit dd8086
}
Packit dd8086
Packit dd8086
/* Added by SukkoPera to allow CDB length to be specified manually */
Packit dd8086
/**
Packit dd8086
   Run a Multimedia command (MMC) specifying the CDB length.
Packit dd8086
   The motivation here is for example ot use in is an undocumented
Packit dd8086
   debug command for LG drives (namely E7), whose length is being
Packit dd8086
   miscalculated by mmc_get_cmd_len(); it doesn't follow the usual
Packit dd8086
   code number to length conventions. Patch supplied by SukkoPera.
Packit dd8086
Packit dd8086
   @param p_cdio        CD structure set by cdio_open().
Packit dd8086
   @param i_timeout_ms  time in milliseconds we will wait for the command
Packit dd8086
                        to complete.
Packit dd8086
   @param p_cdb         CDB bytes. All values that are needed should be set
Packit dd8086
                         on input.
Packit dd8086
   @param i_cdb         number of CDB bytes.
Packit dd8086
   @param e_direction   direction the transfer is to go.
Packit dd8086
   @param i_buf         Size of buffer
Packit dd8086
   @param p_buf         Buffer for data, both sending and receiving.
Packit dd8086
Packit dd8086
   @return 0 if command completed successfully.
Packit dd8086
*/
Packit dd8086
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_run_cmd_len( const CdIo_t *p_cdio, unsigned int i_timeout_ms,
Packit dd8086
                  const mmc_cdb_t *p_cdb, unsigned int i_cdb,
Packit dd8086
                  cdio_mmc_direction_t e_direction, unsigned int i_buf,
Packit dd8086
                  /*in/out*/ void *p_buf )
Packit dd8086
{
Packit dd8086
  if (!p_cdio) return DRIVER_OP_UNINIT;
Packit dd8086
  if (!p_cdio->op.run_mmc_cmd) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
  return p_cdio->op.run_mmc_cmd(p_cdio->env, i_timeout_ms,
Packit dd8086
                                     i_cdb,
Packit dd8086
                                     p_cdb, e_direction, i_buf, p_buf);
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
  See if CD-ROM has feature with value value
Packit dd8086
  @return true if we have the feature and false if not.
Packit dd8086
*/
Packit dd8086
bool_3way_t
Packit dd8086
mmc_have_interface( CdIo_t *p_cdio, cdio_mmc_feature_interface_t e_interface )
Packit dd8086
{
Packit dd8086
  int i_status;                  /* Result of MMC command */
Packit dd8086
  uint8_t buf[65530] = { 0, };   /* Place to hold returned data */
Packit dd8086
  mmc_cdb_t cdb = {{0, }};  /* Command Descriptor Buffer */
Packit dd8086
Packit dd8086
  if (!p_cdio || !p_cdio->op.run_mmc_cmd) return nope;
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_GET_CONFIGURATION);
Packit dd8086
  CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf));
Packit dd8086
Packit dd8086
  cdb.field[1] = CDIO_MMC_GET_CONF_NAMED_FEATURE;
Packit dd8086
  cdb.field[3] = CDIO_MMC_FEATURE_CORE;
Packit dd8086
Packit dd8086
  i_status = mmc_run_cmd(p_cdio, 0, &cdb, SCSI_MMC_DATA_READ, sizeof(buf),
Packit dd8086
                         &buf;;
Packit dd8086
  if (DRIVER_OP_SUCCESS == i_status) {
Packit dd8086
    uint8_t *p;
Packit dd8086
    uint32_t i_data;
Packit dd8086
    uint8_t *p_max = buf + 65530;
Packit dd8086
Packit dd8086
    i_data = (unsigned int) CDIO_MMC_GET_LEN32(buf);
Packit dd8086
    /* set to first sense feature code, and then walk through the masks */
Packit dd8086
    p = buf + 8;
Packit dd8086
    while( (p < &(buf[i_data])) && (p < p_max) ) {
Packit dd8086
      uint16_t i_feature;
Packit dd8086
      uint8_t i_feature_additional = p[3];
Packit dd8086
Packit dd8086
      i_feature = CDIO_MMC_GET_LEN16(p);
Packit dd8086
      if (CDIO_MMC_FEATURE_CORE == i_feature) {
Packit dd8086
        uint8_t *q = p+4;
Packit dd8086
        uint32_t i_interface_standard = CDIO_MMC_GET_LEN32(q);
Packit dd8086
        if (e_interface == i_interface_standard) return yep;
Packit dd8086
      }
Packit dd8086
      p += i_feature_additional + 4;
Packit dd8086
    }
Packit dd8086
    return nope;
Packit dd8086
  } else
Packit dd8086
    return dunno;
Packit dd8086
}
Packit dd8086
Packit dd8086
/**
Packit dd8086
    Read sectors using SCSI-MMC GPCMD_READ_CD.
Packit dd8086
    Can read only up to 25 blocks.
Packit dd8086
*/
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_read_sectors ( const CdIo_t *p_cdio, void *p_buf, lsn_t i_lsn,
Packit dd8086
                   int sector_type, uint32_t i_blocks )
Packit dd8086
{
Packit dd8086
  mmc_cdb_t cdb = {{0, }};
Packit dd8086
Packit dd8086
  mmc_run_cmd_fn_t run_mmc_cmd;
Packit dd8086
Packit dd8086
  if (!p_cdio) return DRIVER_OP_UNINIT;
Packit dd8086
  if (!p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED;
Packit dd8086
Packit dd8086
  run_mmc_cmd = p_cdio->op.run_mmc_cmd;
Packit dd8086
Packit dd8086
  CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_CD);
Packit dd8086
  CDIO_MMC_SET_READ_TYPE    (cdb.field, sector_type);
Packit dd8086
  CDIO_MMC_SET_READ_LBA     (cdb.field, i_lsn);
Packit dd8086
  CDIO_MMC_SET_READ_LENGTH24(cdb.field, i_blocks);
Packit dd8086
  CDIO_MMC_SET_MAIN_CHANNEL_SELECTION_BITS(cdb.field,
Packit dd8086
					   CDIO_MMC_MCSB_ALL_HEADERS);
Packit dd8086
Packit dd8086
  return run_mmc_cmd (p_cdio->env, mmc_timeout_ms,
Packit dd8086
                      mmc_get_cmd_len(cdb.field[0]), &cdb,
Packit dd8086
                      SCSI_MMC_DATA_READ,
Packit dd8086
                      CDIO_CD_FRAMESIZE_RAW * i_blocks,
Packit dd8086
                      p_buf);
Packit dd8086
}
Packit dd8086
Packit dd8086
driver_return_code_t
Packit dd8086
mmc_set_blocksize ( const CdIo_t *p_cdio, uint16_t i_blocksize)
Packit dd8086
{
Packit dd8086
  if ( ! p_cdio )  return DRIVER_OP_UNINIT;
Packit dd8086
  return
Packit dd8086
    mmc_set_blocksize_private (p_cdio->env, p_cdio->op.run_mmc_cmd,
Packit dd8086
                               i_blocksize);
Packit dd8086
}
Packit dd8086
Packit dd8086

Packit dd8086
/*
Packit dd8086
 * Local variables:
Packit dd8086
 *  c-file-style: "gnu"
Packit dd8086
 *  tab-width: 8
Packit dd8086
 *  indent-tabs-mode: nil
Packit dd8086
 * End:
Packit dd8086
 */