/* Common Multimedia Command (MMC) routines. $Id: mmc.c,v 1.11 2005/02/11 03:30:12 rocky Exp $ Copyright (C) 2004, 2005 Rocky Bernstein 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 2 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, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include "cdio_private.h" #ifdef HAVE_STRING_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_STDIO_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #define DEFAULT_TIMEOUT_MS 6000 /************************************************************************* 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. *************************************************************************/ /*! 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); } /*! Get the lsn of the end of the CD (via MMC). @return the lsn. On error return CDIO_INVALID_LSN. */ 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 ); } /*! 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 */ 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 ); } /* Set read blocksize (via MMC) */ driver_return_code_t set_blocksize_mmc (void *p_user_data, int 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); } /* Set CD-ROM drive speed (via MMC) */ 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 ); } /*! 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. */ 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; /* 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; } /*! 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) { scsi_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, DEFAULT_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; } /*! Return the media catalog number MCN. Note: string is malloc'd so caller should free() then returned string when done with it. */ char * mmc_get_mcn_private ( void *p_env, const mmc_run_cmd_fn_t run_mmc_cmd ) { scsi_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); cdb.field[1] = 0x0; cdb.field[2] = 0x40; cdb.field[3] = CDIO_SUBCHANNEL_MEDIA_CATALOG; CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); i_status = run_mmc_cmd(p_env, DEFAULT_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; } /* Run a MODE SENSE command (either the 6- or 10-byte version @return DRIVER_OP_SUCCESS if we ran the command ok. */ int mmc_mode_sense( CdIo_t *p_cdio, /*out*/ void *p_buf, int i_size, int page) { /* We used to make a choice as to which routine we'd use based cdio_have_atapi(). But since that calls this in its determination, we had an infinite recursion. So we can't use cdio_have_atapi() (until we put in better capability checks.) */ if ( DRIVER_OP_SUCCESS == mmc_mode_sense_6(p_cdio, p_buf, i_size, page) ) return DRIVER_OP_SUCCESS; return mmc_mode_sense_10(p_cdio, p_buf, i_size, page); } /*! Run a MODE_SENSE command (6-byte version) and put the results in p_buf @return DRIVER_OP_SUCCESS if we ran the command ok. */ int mmc_mode_sense_6( CdIo_t *p_cdio, void *p_buf, int i_size, int page) { scsi_mmc_cdb_t cdb = {{0, }}; if ( ! p_cdio ) return DRIVER_OP_UNINIT; if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; memset (p_buf, 0, i_size); CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_6); cdb.field[2] = 0x3F & page; cdb.field[4] = i_size; return p_cdio->op.run_mmc_cmd (p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, i_size, p_buf); } /*! Run a MODE_SENSE command (10-byte version) and put the results in p_buf @return DRIVER_OP_SUCCESS if we ran the command ok. */ int mmc_mode_sense_10( CdIo_t *p_cdio, void *p_buf, int i_size, int page) { scsi_mmc_cdb_t cdb = {{0, }}; if ( ! p_cdio ) return DRIVER_OP_UNINIT; if ( ! p_cdio->op.run_mmc_cmd ) return DRIVER_OP_UNSUPPORTED; memset (p_buf, 0, i_size); CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_MODE_SENSE_10); cdb.field[2] = 0x3F & page; CDIO_MMC_SET_READ_LENGTH16(cdb.field, i_size); return p_cdio->op.run_mmc_cmd (p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, i_size, p_buf); } /* 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; scsi_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); cdb.field[1] = CDIO_CDROM_MSF; /* Format */ cdb.field[2] = CDIO_MMC_READTOC_FMT_CDTEXT; /* Setup to read header, to get length of data */ CDIO_MMC_SET_READ_LENGTH16(cdb.field, 4); errno = 0; /* Set read timeout 3 minues. */ #define READ_TIMEOUT 3*60*1000 /* 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, READ_TIMEOUT, 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, READ_TIMEOUT, 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, set_cdtext_field_fn); } } driver_return_code_t mmc_set_blocksize_private ( void *p_env, const mmc_run_cmd_fn_t run_mmc_cmd, unsigned int i_bsize) { scsi_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_bsize >> 16) & 0xff; mh.block_length_med = (i_bsize >> 8) & 0xff; mh.block_length_lo = (i_bsize >> 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, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, sizeof(mh), &mh); } /*********************************************************** User-accessible Operations. ************************************************************/ /*! Return the number of length in bytes of the Command Descriptor buffer (CDB) for a given SCSI 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)]; } /*! Return the size of the CD in logical block address (LBA) units. @return the lsn. On error 0 or CDIO_INVALD_LSN. */ lsn_t mmc_get_disc_last_lsn ( const CdIo_t *p_cdio ) { scsi_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, DEFAULT_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; } /*! 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 SCSI MMC draft specification, revision 10a at http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf See especially tables 72, 73 and 75. */ discmode_t mmc_get_discmode( const CdIo_t *p_cdio ) { uint8_t buf[14] = { 0, }; scsi_mmc_cdb_t cdb; memset(&cdb, 0, sizeof(scsi_mmc_cdb_t)); CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_READ_TOC); cdb.field[1] = CDIO_CDROM_MSF; /* The MMC-5 spec may require this. */ cdb.field[2] = CDIO_MMC_READTOC_FMT_FULTOC; CDIO_MMC_SET_READ_LENGTH8(cdb.field, sizeof(buf)); 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; } 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) { if ( ! p_cdio ) return; /* 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; 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; } /*! Get the DVD type associated with cd object. */ 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); } /*! Get the CD-ROM hardware info via a SCSI MMC INQUIRY command. False is returned if we had an error getting the information. */ bool mmc_get_hwinfo ( const CdIo_t *p_cdio, /*out*/ cdio_hwinfo_t *hw_info ) { int i_status; /* Result of SCSI MMC command */ char buf[36] = { 0, }; /* Place to hold returned data */ scsi_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, DEFAULT_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; } /*! 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 */ int mmc_get_media_changed(const CdIo_t *p_cdio) { scsi_mmc_cdb_t cdb = {{0, }}; uint8_t buf[8] = { 0, }; int i_status; 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_GET_EVENT_STATUS); cdb.field[1] = 1; /* We poll for info */ cdb.field[4] = 1 << 4; /* We want Media events */ /* Setup to read header, to get length of data */ CDIO_MMC_SET_READ_LENGTH16(cdb.field, sizeof(buf)); i_status = p_cdio->op.run_mmc_cmd(p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_READ, sizeof(buf), buf); if(i_status == 0) { return 0 != (buf[4] & 0x02); } return DRIVER_OP_ERROR; } 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 ); } /*! Run a SCSI MMC command. cdio CD structure set by cdio_open(). i_timeout time in milliseconds we will wait for the command to complete. If this value is -1, use the default time-out value. buf Buffer for data, both sending and receiving len Size of buffer e_direction direction the transfer is to go cdb CDB bytes. All values that are needed should be set on input. We'll figure out what the right CDB length should be. */ driver_return_code_t mmc_run_cmd( const CdIo_t *p_cdio, unsigned int i_timeout_ms, const scsi_mmc_cdb_t *p_cdb, scsi_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, scsi_mmc_get_cmd_len(p_cdb->field[0]), p_cdb, e_direction, i_buf, p_buf); } int mmc_get_blocksize ( CdIo_t *p_cdio) { int i_status; 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; uint8_t *p = &mh.block_length_med; memset (&mh, 0, sizeof (mh)); i_status = mmc_mode_sense_6(p_cdio, &mh, sizeof(&mh), CDIO_MMC_R_W_ERROR_PAGE); if (DRIVER_OP_SUCCESS != i_status) return i_status; return CDIO_MMC_GET_LEN16(p); } /*! * Eject using SCSI MMC commands. Return 0 if successful. */ driver_return_code_t mmc_eject_media( const CdIo_t *p_cdio ) { int i_status = 0; scsi_mmc_cdb_t cdb = {{0, }}; uint8_t buf[1]; 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_ALLOW_MEDIUM_REMOVAL); i_status = run_mmc_cmd (p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, 0, &buf); if (0 != i_status) return i_status; CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); cdb.field[4] = 1; i_status = run_mmc_cmd (p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, 0, &buf); if (0 != i_status) return i_status; CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_START_STOP); cdb.field[4] = 2; /* eject */ return run_mmc_cmd (p_cdio->env, DEFAULT_TIMEOUT_MS, mmc_get_cmd_len(cdb.field[0]), &cdb, SCSI_MMC_DATA_WRITE, 0, &buf); } /*! Return a string containing the name of the given feature */ 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; } } } /*! 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: return "Re-recordable DVD using Sequential recording"; case CDIO_MMC_FEATURE_PROF_DVD_PRW: return "DVD+RW - DVD ReWritable"; 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_DVD_PR2: return "DVD+R Double Layer - DVD Recordable Double Layer"; 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"; 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; } } } /*! * See if CD-ROM has feature with value value * @return true if we have the feature and false if not. */ bool_3way_t mmc_have_interface( CdIo_t *p_cdio, mmc_feature_interface_t e_interface ) { int i_status; /* Result of MMC command */ uint8_t buf[500] = { 0, }; /* Place to hold returned data */ scsi_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; } /*! Read sectors using SCSI-MMC GPCMD_READ_CD. Can read only up to 25 blocks. */ driver_return_code_t mmc_read_sectors ( const CdIo_t *p_cdio, void *p_buf, lba_t lba, int sector_type, unsigned int i_blocks ) { scsi_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, lba); 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, DEFAULT_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, unsigned int 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. @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_speed ) { uint8_t buf[14] = { 0, }; scsi_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_speed < 1 ) return -1; memset(&cdb, 0, sizeof(scsi_mmc_cdb_t)); CDIO_MMC_SET_COMMAND(cdb.field, CDIO_MMC_GPCMD_SET_SPEED); CDIO_MMC_SET_LEN16(cdb.field, 2, i_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_READ, sizeof(buf), buf); } /* * Local variables: * c-file-style: "gnu" * tab-width: 8 * indent-tabs-mode: nil * End: */