misc_scsi.c revision 4d218355460e094792d6bcc161b586d7389d0d83
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <libintl.h>
#include "mmc.h"
#include "util.h"
#include "misc_scsi.h"
#include "transport.h"
#include "main.h"
#include "toshiba.h"
#include "msgs.h"
#include "device.h"
struct track_info *tip);
int *sess_nump);
int sess_num, int *last_trk_nump);
read_scsi32(void *addr)
{
return (ret);
}
read_scsi16(void *addr)
{
return (ret);
}
void
{
}
void
{
}
/*
* will get the mode page only i.e. will strip off the header.
*/
int
{
int ret;
/* Ask 254 bytes only to make our IDE driver happy */
if (ret == 0) {
return (0);
}
return (1);
}
/*
* will take care of adding mode header and any extra bytes at the end.
*/
int
{
int ret;
if (debug) {
int i;
(void) printf("MODE: [");
for (i = 0; i < p_len; i++) {
}
(void) printf("]\n");
}
return (ret);
}
/*
* Builds track information database for track trackno. If trackno is
* -1, builds the database for next blank track.
*/
int
{
int ret;
/* 1st try READ TRACK INFORMATION */
/* Gererate faked information for writing to DVD */
if (device_type != CD_RW) {
t_info->ti_start_address = 0;
/* only 1 track on DVD make it max size */
&bsize);
}
t_info->ti_free_blocks = 0;
}
if (debug)
(void) printf("using read_track_info for TOC \n");
else
return (1);
}
/* READ TRACK INFORMATION not supported, try other options */
/*
* We can get info for next blank track if READ TRACK INFO is not
* supported.
*/
if (trackno == -1)
return (0);
if (debug)
(void) printf("using READ_TOC for TOC\n");
/* Try Read TOC */
return (0);
}
/* Numbers for audio tracks are always in 2K chunks */
}
/* Now find out the session thing */
/*
* Make sure that the call succeeds and returns the requested
* TOC size correctly.
*/
/* For ATAPI drives or old Toshiba drives */
}
/* If this goes through well TOC length will always be 0x0a */
}
/*
* This might be the last track of this session. If so,
* exclude the leadout and next lead in.
*/
/*
* 1.5 Min leadout + 1 min. leadin + 2 sec. pre-gap.
* For 2nd+ leadout it will be 0.5 min. But currently
* there is no direct way. And it will not happen
* for any normal case.
*
* ((1.5 +1)*60 + 2)*75 = 11400 frames (blocks)
*/
}
} else {
return (0);
}
return (1);
}
/*
* The size of the last track in one of the first N - 1 sessions of an
* N-session (N > 1) disc is reported incorrectly by some drives and calculated
* a session is erroneously considered part of that track. This function checks
* for this corner case, and adjusts the track size if necessary.
*/
static int
{
int sess_last_trk_num;
int trk_sess_num;
/* Request Raw TOC Header for session count. */
return (0);
/* Is this a multi-session medium? */
/* Yes; request entire Raw TOC. */
!= 1)
goto fail;
&trk_sess_num) != 1)
goto fail;
/* Is the track in one of the first N - 1 sessions? */
goto fail;
/* Is the track the last track in the session? */
if (trk_num == sess_last_trk_num) {
&sess_leadout_lba) != 1)
goto fail;
/* Fix track size if it was too big. */
}
}
}
return (1);
fail:
return (0);
}
/*
* Determine what session number a track is in by parsing the Raw TOC format of
*/
static int
int *sess_nump)
{
sizeof (rtoc_td_t));
NULL) {
return (1);
} else
return (0);
}
/*
* Determine the last track number in a specified session number by parsing the
*/
static int
int *last_trk_nump)
{
sizeof (rtoc_td_t));
POINT_SESS_LAST_TRK)) != NULL) {
return (1);
} else {
++tdp;
}
}
return (0);
}
/*
* Determine the starting LBA of the the session leadout by parsing the Raw TOC
*/
static int
{
sizeof (rtoc_td_t));
POINT_LEADOUT_ADDR)) != NULL) {
return (1);
} else {
++tdp;
}
}
return (0);
}
/*
* Search a set of Raw TOC Track Descriptors using <'adr', 'point'> as the
* search key. Return a pointer to the first Track Descriptor that matches.
*/
static rtoc_td_t *
{
return (cur_tdp);
else
cur_tdp++;
}
return (NULL);
}
{
int ret;
if (ret == 0)
mode = 0xff;
else
return (mode);
}
/*
* Set page code 5 for TAO mode.
*/
int
int keep_disc_open)
{
int no_err;
int reset_device;
"Multi-session is not supported on DVD media\n"));
exit(1);
}
(void) printf("Preparing to write in DAO\n");
}
/* Some drives do not support this command but still do it */
if (no_err)
/*
* If the device is already in simulation mode and again a
* simulation is requested, then set the device in non-simulation
* 1st and then take it to simulation mode. This will flush any
* previous fake state in the drive.
*/
reset_device = 1;
} else {
reset_device = 0;
}
if (no_err != 0) {
buf[0] &= 0x3f;
/* set TAO or DAO writing mode */
/* set simulation flag */
if (test_write && (!reset_device)) {
} else {
}
/* Turn on HW buffer underrun protection (BUFE) */
if (!test_write) {
}
/* set track mode type */
if (device_type == CD_RW) {
}
if (keep_disc_open) {
}
/* Select track type (audio or data) */
if (track_mode == TRACK_MODE_DATA) {
} else {
}
/* Need to clear these fields for setting into DAO */
if (write_mode == DAO_MODE)
/* print out mode for detailed log */
int i;
(void) printf("setting = [ ");
for (i = 0; i < 15; i++)
(void) printf("]\n");
}
if (no_err && reset_device) {
/* Turn the test write bit back on */
}
/*
* Since BUFE is the only optional flag we are
* setting we will try to turn it off if the command
* fails.
*/
if (!no_err) {
/*
* Some old drives may not support HW
* buffer underrun protection, try again
* after turning it off.
*/
if (debug)
(void) printf("Turning off BUFE\n");
}
}
return (no_err);
}
/*
* Close session. This will write TOC.
*/
int
{
int immediate;
int finalize_max;
/*
* For ATAPI devices we will use the immediate mode and will
* poll the command for completion so that this command may
* not hog the channel. But for SCSI, we will use the treditional
* way of issuing the command with a large enough timeout. This
* is done because immediate mode was designed for ATAPI and some
* SCSI RW drives might not be even tested with it.
*/
/* SCSI device */
immediate = 0;
} else {
/* non-SCSI (e.g ATAPI) device */
immediate = 1;
}
/* We need to close track before close session */
if (device_type == DVD_PLUS) {
return (0);
}
/*
* For DAO mode which we use for DVD-RW, the latest MMC
* specification does not mention close_track. Some
* newer drives will return an ILLEGAL INSTRUCTION
* which we will ignore. We have also found a Panasonic
* drive which will return a MEDIA ERROR. It is safe
* to ignore both errors as this is not needed for
* these drives.
* This is kept for older drives which had needed
* us to issue close_track to flush the cache fully.
* once we are certain these drives have cleared the
* market, this can be removed.
*/
if (device_type == DVD_MINUS) {
return (0);
}
} else {
if (!immediate)
return (1);
}
if (immediate) {
(void) sleep(10);
err = 0;
if (device_type == CD_RW) {
/* Finalization should not take more than 6 minutes */
} else {
/* some DVD-RW drives take longer than 6 minutes */
}
if (ret != 0)
break;
if (uscsi_status != 2)
err = 1;
/* not ready but not becoming ready */
err = 1;
/* illegal mode for this track */
err = 1;
} else {
err = 1;
}
if (err == 1) {
if (debug) {
(void) printf("Finalization failed\n");
(void) printf("%x %x %x %x\n",
}
return (0);
}
if (uscsi_status == 2) {
int i;
/* illegal field in command packet */
/* print it out! */
(void) printf("\n");
for (i = 0; i < 18; i++)
(void) printf("%x ",
(unsigned)(rqbuf[i]));
(void) printf("\n");
}
}
(void) sleep(5);
}
}
return (ret);
}
/*
* Find out media capacity.
*/
{
return (0);
}
/*
* If we have a DVD+R this field is an LBA. If the media is
* a CD-R/W the field is MSF formatted. Otherwise this field
* is not valid and will be zero.
*/
if (device_type == DVD_PLUS) {
} else {
cap = 0;
}
} else {
} else {
cap = 0;
}
}
return (cap);
}
int
{
int retry;
int ret;
if (ret)
break;
}
return (ret);
}
int
{
if (vol_running) {
/* If there is a media, try using DKIOCEJECT 1st */
/*
* The check_device() call will issue
* a TEST UNIT READY (TUR) and retry many
* times when a DVD-R is present. The DKIOCEJECT
* ioctl will subsequently fail causing us to
* with out ejecting the media. Insted of letting
* this happen, issue a reset to the device before
* issuing the DKIOCEJCET ioctl.
*/
if (device_type == DVD_MINUS)
return (1);
}
}
}
/* if eject fails */
/*
* check that eject is not blocked on the device
*/
return (0);
}
return (0);
}
return (1);
}
/*
* Get current Read or Write Speed from Mode Page 0x2a.
*
* Use the size of the Page to determine which Multimedia Command
* set (MMC) is present. Based on the MMC version, get the
*
* Note that some MMC versions do not necessarily support a
* (current) Read or Write Speed. As a result, this function
* _can_ return a value of zero.
*
* The newer standards (reserve and) mark the field(s) as Obsolete,
* yet many vendors populate the Obsolete fields with valid values
* (assumedly for backward compatibility). This is important, as
* a command like GET PERFORMANCE cannot return _the_ speed; it can
* only return a Logical-Block-Address-dependent (LBA) speed. Such
* values can vary widely between the innermost and outermost Track.
* Mode Page 0x2a is the best solution identifying "the current
* (nominal) speed".
*/
static uint16_t
{
int offset;
/*
* Allocate a buffer acceptably larger than any nominal
* Page for Page Code 0x2A.
*/
goto end;
/* Determine MMC version based on 'Page Length' field */
switch (mp2a[1]) {
case 0x14: /* MMC-1 */
if (debug)
(void) printf("Mode Page 2A: MMC-1\n");
break;
case 0x18: /* MMC-2 */
if (debug)
(void) printf("Mode Page 2A: MMC-2;"
" Read and Write Speeds are "
"obsolete\n");
/* see if "Obsolete" values are valid: */
break;
default: /* MMC-3 or newer */
if (debug)
(void) printf("Mode Page 2A: MMC-3 or"
" newer; Read Speed is obsolete.\n");
if (cmd == GET_READ_SPEED) {
/* this is Obsolete, but try it */
offset = 14;
} else {
/* Write Speed is not obsolete */
offset = 28;
if (rate == 0) {
/*
* then try an Obsolete field
* (but this shouldn't happen!)
*/
offset = 20;
}
}
break;
}
end:
if (debug)
(void) printf("cd_speed_get: %s Speed is "
return (rate);
}
/*
* CD speed related functions (ioctl style) for drives which do not support
* real time streaming.
*
* For the SET operations, the SET CD SPEED command needs
* both the Read Speed and the Write Speed. Eg, if
* we're trying to set the Write Speed (SET_WRITE_SPEED),
* then we first need to obtain the current Read Speed.
* That speed is specified along with the chosen_speed (the
* Write Speed in this case) in the SET CD SPEED command.
*/
int
{
switch (cmd) {
case GET_READ_SPEED:
return (cdrw_bandwidth_to_x(rate));
case GET_WRITE_SPEED:
return (cdrw_bandwidth_to_x(rate));
case SET_READ_SPEED:
break;
case SET_WRITE_SPEED:
break;
default:
return (0);
}
}
/*
* Manage sending of SET STREAMING command using the specified
* read_speed and write_speed.
*
* This function allocates and initializes a Performance
* Descriptor, which is sent as part of the SET STREAMING
* command. The descriptor is deallocated before function
* exit.
*/
static int
{
int ret;
/* Allocate and initialize the Performance Descriptor */
/* Read Time (in milliseconds) */
/* Write Time (in milliseconds) */
/* Read Speed */
/* Write Speed */
/* issue SET STREAMING command */
return (ret);
}
/*
* cd speed related functions for drives which support
* Real-Time Streaming Feature.
*
* For the SET operations, the SET STREAMING command needs
* both the Read Speed and the Write Speed. Eg, if
* we're trying to set the Write Speed (SET_WRITE_SPEED),
* then we first need to obtain the current Read Speed.
* That speed is specified along with the chosen_speed (the
* Write Speed in this case) in the SET STREAMING command.
*/
int
{
int ret = 0;
switch (cmd) {
case GET_WRITE_SPEED:
break;
case GET_READ_SPEED:
break;
case SET_READ_SPEED: {
/* set Read Speed using SET STREAMING */
/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
if (ret == 0) {
if (debug)
(void) printf(" real time speed control"
" failed, using CD speed control\n");
}
break;
}
case SET_WRITE_SPEED: {
/* set Write Speed using SET STREAMING */
/* If rt_speed_ctrl fails for any reason use cd_speed_ctrl */
if (ret == 0) {
if (debug)
(void) printf(" real time speed control"
" failed, using CD speed control\n");
}
break;
}
default:
break;
}
return (ret);
}
/*
* Initialize device for track-at-once mode of writing. All of the data will
* need to be written to the track without interruption.
* This initialized TAO by setting page code 5 and speed.
*/
void
write_init(int mode)
{
if (simulation)
print_n_flush("...");
/* DVD- requires DAO mode */
if (device_type == DVD_MINUS) {
}
/* DVD+ and DVD- have no support for AUDIO, bail out */
exit(1);
}
if (simulation &&
device_type != DVD_PLUS_W) {
/*
* If we were in simulation mode, and media wasn't blank,
* but medium was erasable, then cdrw goes to erase the
* contents of the media after the simulation writing in order
* to cleanup the ghost TOC (see write_fini() calls blank()).
* This is bad because it removes existing data if media was
* multi-session. Therefore, we no longer allow simulation
* writing if such condition is met. we don't blank the DVD+RW
* media, so DVD+RWs are fine.
*/
"Cannot perform simulation for non-blank media\n"));
exit(1);
}
/* l10n_NOTE : 'failed' as in Initializing device...failed */
exit(1);
}
/* l10n_NOTE : 'done' as in "Initializing device...done" */
/* if speed change option was used (-p) then try to set the speed */
if (requested_speed != 0) {
if (verbose)
requested_speed) == 0) {
exit(1);
}
if (verbose) {
int speed;
GET_WRITE_SPEED, 0);
if (speed == requested_speed) {
speed);
} else if (speed == 0) {
"current Write Speed.\n"));
} else {
(void) printf(
gettext("Speed set to closest approximation "
"of %dX allowed by device (%dX).\n"),
}
}
}
}
void
write_fini(void)
{
/* Some drives don't like this while in test write mode */
if (!simulation) {
/*
* It is possible that the drive is busy writing the
* buffered portion. So do not get upset yet.
*/
(void) sleep(10);
if (debug) {
(void) printf("status %x, %x/%x/%x\n",
}
/*
* Different vendor drives return different
* sense error info for CLOSE SESSION command.
* The Panasonic drive that we are using is
* one such drive.
*/
if (device_type == DVD_MINUS) {
if (verbose) {
(void) printf(
"skipping finalizing\n");
}
} else {
/* l10n_NOTE : 'failed' as in finishing up...failed */
"Could not finalize the disc.\n"));
exit(1);
}
}
}
if (vol_running) {
(void) eject_media(target);
}
/*
* Some drives such as the pioneer A04 will retain a
* ghost TOC after a simulation write is done. The
* media will actually be blank, but the drive will
* report a TOC. There is currently no other way to
* re-initialize the media other than ejecting or
* to ask the drive to clear the leadout. The laser
* is currently off so nothing is written to the
* media (on a good behaving drive).
* NOTE that a device reset does not work to make
* the drive re-initialize the media.
*/
blanking_type = "clear_ghost";
blank();
}
/* l10n_NOTE : 'done' as in "Finishing up...done" */
}