libburn/libburn/spc.c

2217 lines
57 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
Copyright (c) 2006 - 2019 Thomas Schmitt <scdbackup@gmx.net>
Provided under GPL version 2 or later.
*/
/* scsi primary commands */
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <string.h>
/* ts A61008 */
/* #include <a ssert.h> */
#include <stdlib.h>
#include "libburn.h"
#include "transport.h"
#include "spc.h"
#include "mmc.h"
#include "sbc.h"
#include "drive.h"
#include "debug.h"
#include "options.h"
#include "init.h"
#include "util.h"
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
/* ts A91111 :
whether to log SCSI commands:
bit0= log in /tmp/libburn_sg_command_log
bit1= log to stderr
bit2= flush every line
*/
extern int burn_sg_log_scsi;
/* spc command set */
/* ts A70519 : allocation length byte 3+4 was 0,255 */
static unsigned char SPC_INQUIRY[] = { 0x12, 0, 0, 0, 36, 0 };
/*static char SPC_TEST[]={0,0,0,0,0,0};*/
static unsigned char SPC_PREVENT[] = { 0x1e, 0, 0, 0, 1, 0 };
static unsigned char SPC_ALLOW[] = { 0x1e, 0, 0, 0, 0, 0 };
static unsigned char SPC_MODE_SENSE[] = { 0x5a, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char SPC_MODE_SELECT[] = { 0x55, 16, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char SPC_REQUEST_SENSE[] = { 0x03, 0, 0, 0, 18, 0 };
static unsigned char SPC_TEST_UNIT_READY[] = { 0x00, 0, 0, 0, 0, 0 };
#ifdef Libburn_enable_scsi_cmd_ABh
static unsigned char SPC_READ_MEDIA_SERIAL_NUMBER[] =
{ 0xAB, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
#endif
/* ts A70519 : An initializer for the abstract SCSI command structure */
int scsi_init_command(struct command *c, unsigned char *opcode, int oplen)
{
if (oplen > 16)
return 0;
memset(c, 0, sizeof(struct command));
memcpy(c->opcode, opcode, oplen);
c->oplen = oplen;
c->dir = NO_TRANSFER;
c->dxfer_len = -1;
memset(c->sense, 0, sizeof(c->sense));
c->sense_len = 0;
c->error = 0;
c->retry = 0;
c->page = NULL;
c->timeout = Libburn_scsi_default_timeouT;
c->start_time = c->end_time = 0.0;
c->retry_count = 0;
c->last_retry_key = 0;
c->last_retry_asc = 0;
c->last_retry_ascq = 0;
return 1;
}
/* ts B00728 */
int spc_decode_sense(unsigned char *sense, int senselen,
int *key, int *asc, int *ascq)
{
*key = *asc = *ascq = 0;
if ((sense[0] & 0x7f) == 0x72 || (sense[0] & 0x7f) == 0x73) {
if (senselen <= 0 || senselen > 1)
*key = sense[1] & 0x0f;
if (senselen <= 0 || senselen > 2)
*asc = sense[2];
if (senselen <= 0 || senselen > 3)
*ascq = sense[3];
return 1;
}
if (senselen <= 0 || senselen > 2)
*key = sense[2] & 0x0f;
if (senselen <= 0 || senselen > 12)
*asc = sense[12];
if (senselen <= 0 || senselen > 13)
*ascq = sense[13];
return 1;
}
int spc_test_unit_ready_r(struct burn_drive *d, int *key, int *asc, int *ascq,
int *progress)
{
struct command *c;
c = &(d->casual_command);
if (mmc_function_spy(d, "test_unit_ready") <= 0)
return 0;
scsi_init_command(c, SPC_TEST_UNIT_READY,sizeof(SPC_TEST_UNIT_READY));
c->retry = 0;
c->dir = NO_TRANSFER;
d->issue_command(d, c);
*key = *asc = *ascq = 0;
*progress = -1;
if (c->error) {
spc_decode_sense(c->sense, 0, key, asc, ascq);
if (c->sense[0] == 0x70 &&
((c->sense[2] & 0x0f) == 0 || (c->sense[2] & 0x0f) == 2) &&
(c->sense[15] & 0x80))
*progress = (c->sense[16] << 8) + c->sense[17];
return (*key == 0);
}
return 1;
}
int spc_test_unit_ready(struct burn_drive *d)
{
int key, asc, ascq, progress;
return spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress);
}
/* ts A70315 */
/** @param flag bit0=do not wait 0.1 seconds before first test unit ready
bit1=do not issue success message
*/
/** Wait until the drive state becomes clear or until max_usec elapsed */
int spc_wait_unit_attention(struct burn_drive *d, int max_sec, char *cmd_text,
int flag)
{
int i, ret = 1, key = 0, asc = 0, ascq = 0, clueless_start = 0;
static double tests_per_second = 2.0;
int sleep_usecs, loop_limit, clueless_timeout, progress;
char *msg = NULL, *cmd_name = NULL, *cmd_cpt;
unsigned char sense[14];
BURN_ALLOC_MEM(msg, char, 320);
BURN_ALLOC_MEM(cmd_name, char, 320);
clueless_timeout = 5 * tests_per_second + 1;
loop_limit = max_sec * tests_per_second + 1;
sleep_usecs = 1000000 / tests_per_second;
strcpy(cmd_name, cmd_text);
cmd_cpt = strchr(cmd_name, ':');
if (cmd_cpt != NULL)
*cmd_cpt = 0;
if (!(flag & 1))
usleep(sleep_usecs);
for(i = !(flag & 1); i < loop_limit; i++) {
ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress);
if (ret > 0) /* ready */
break;
if (key!=0x2 || asc!=0x4) {
if (key == 0x2 && asc == 0x3A) {
ret = 1; /* medium not present = ok */
/* <<<
ts A70912 :
My LG GSA-4082B on asynchronous load:
first it reports no media 2,3A,00,
then it reports not ready 2,04,00,
further media inquiry retrieves wrong data
if(i<=100)
goto slumber;
*/
break;
}
if (key == 0x6 && asc == 0x28)
/* medium change notice or alike = try again */
goto slumber;
handle_error:;
/* ts A90213 */
sprintf(msg,
"Asynchronous SCSI error on %s: ", cmd_name);
sense[0] = 0x70; /* Fixed format sense data */
sense[2] = key;
sense[12] = asc;
sense[13] = ascq;
scsi_error_msg(d, sense, 14, msg + strlen(msg),
&key, &asc, &ascq);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002014d,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
if (cmd_cpt != NULL) {
sprintf(msg, "Attempted SCSI CDB: %s",
cmd_cpt + 1);
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x0002014d,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
}
d->cancel = 1;
break;
} else if (ascq == 0x00) { /* CAUSE NOT REPORTABLE */
/* Might be a clueless system adapter */
if (clueless_start == 0)
clueless_start = i;
if (i - clueless_start > clueless_timeout) {
libdax_msgs_submit(libdax_messenger,
d->global_index,
0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
"Ended clueless NOT READY cycle",
0, 0);
if (cmd_cpt != NULL) {
sprintf(msg, "Attempted SCSI CDB: %s",
cmd_cpt + 1);
libdax_msgs_submit(libdax_messenger,
d->global_index,
0x00000002,
LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
}
ret = 1; /* medium not present = ok */
break;
}
} else if (ascq == 0x02 || ascq == 0x03)
goto handle_error;
slumber:;
usleep(sleep_usecs);
}
if (ret <= 0 || !(flag & 2)) {
sprintf(msg, "Async %s %s after %d.%d seconds",
cmd_name, (ret > 0 ? "succeeded" : "failed"),
i / 10, i % 10);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020150, LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_LOW, msg, 0, 0);
}
if (i < max_sec * 10)
{ret = (ret > 0); goto ex;}
sprintf(msg, "Timeout (%d s) with asynchronous SCSI command %s\n",
max_sec, cmd_name);
libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014f,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
if (cmd_cpt != NULL) {
sprintf(msg, "Attempted SCSI CDB: %s", cmd_cpt + 1);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002014f, LIBDAX_MSGS_SEV_SORRY,
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
}
ret = 0;
ex:;
BURN_FREE_MEM(msg);
BURN_FREE_MEM(cmd_name);
return ret;
}
void spc_request_sense(struct burn_drive *d, struct buffer *buf)
{
struct command *c;
c = &(d->casual_command);
if (mmc_function_spy(d, "request_sense") <= 0)
return;
scsi_init_command(c, SPC_REQUEST_SENSE, sizeof(SPC_REQUEST_SENSE));
c->retry = 0;
c->dxfer_len= c->opcode[4];
c->retry = 0;
c->page = buf;
c->page->sectors = 0;
c->page->bytes = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
}
static int spc_report_async_error(struct burn_drive *d,
int key, int asc, int ascq, int flag)
{
char *msg = NULL;
unsigned char sense[14];
int ret;
BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160);
sprintf(msg, "Asynchronous SCSI error : ");
sense[0] = 0x70; /* Fixed format sense data */
sense[2] = key;
sense[12] = asc;
sense[13] = ascq;
scsi_error_msg(d, sense, 14, msg + strlen(msg), &key, &asc, &ascq);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x000201a5,
LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
ret = 1;
ex:;
BURN_FREE_MEM(msg);
return ret;
}
/* @return -3 = other error reported
-2 = drive is ready ,
-1 = not ready, but no progress reported ,
>= 0 progress indication between 0 and 65535
*/
int spc_get_erase_progress(struct burn_drive *d)
{
struct buffer *b = NULL;
int ret, key, asc, ascq, progress;
if (mmc_function_spy(d, "get_erase_progress") <= 0)
{ret = 0; goto ex;}
/* ts B20104 :
TEST UNIT READY seems to be more reliable than REQUEST SENSE.
Nevertheless growisofs still uses the latter as fallback.
*/
ret = spc_test_unit_ready_r(d, &key, &asc, &ascq, &progress);
if (ret > 0)
{ret = -2; goto ex;}
/* Check key, asc, ascq for errors other than "not yet ready" */
if (key != 0 &&
(key != 0x2 || asc != 0x04 || ascq == 0x02 || ascq ==0x03)) {
spc_report_async_error(d, key, asc, ascq, 0);
ret= -3; goto ex;
}
if (progress >= 0)
{ret = progress; goto ex;}
/* Fallback to request sense */
BURN_ALLOC_MEM(b, struct buffer, 1);
spc_request_sense(d, b);
/* Checking the preconditions as of SPC-3 4.5.2.4.4 and 4.5.3 */
ret = -1;
if (b->data[0] == 0x70 &&
((b->data[2] & 0x0f) == 0 || (b->data[2] & 0x0f) == 2) &&
(b->data[15] & 0x80))
ret = (b->data[16] << 8) | b->data[17];
ex:;
BURN_FREE_MEM(b);
return ret;
}
void spc_inquiry(struct burn_drive *d)
{
struct buffer *buf = NULL;
struct burn_scsi_inquiry_data *id;
struct command *c = NULL;
if (mmc_function_spy(d, "inquiry") <= 0)
return;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
scsi_init_command(c, SPC_INQUIRY, sizeof(SPC_INQUIRY));
c->dxfer_len = (c->opcode[3] << 8) | c->opcode[4];
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
id = (struct burn_scsi_inquiry_data *)d->idata;
id->peripheral = 0x7f; /* SPC-3: incabable undefined peripheral type */
id->version = 0; /* SPC-3: no claim for conformance */
memset(id->vendor, 0, 9);
memset(id->product, 0, 17);
memset(id->revision, 0, 5);
if (c->error) {
id->valid = -1;
goto ex;
}
id->peripheral = ((char *) c->page->data)[0];
id->version = ((char *) c->page->data)[2];
memcpy(id->vendor, c->page->data + 8, 8);
memcpy(id->product, c->page->data + 16, 16);
memcpy(id->revision, c->page->data + 32, 4);
id->valid = 1;
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
return;
}
void spc_prevent(struct burn_drive *d)
{
struct command *c;
c = &(d->casual_command);
if (mmc_function_spy(d, "prevent") <= 0)
return;
scsi_init_command(c, SPC_PREVENT, sizeof(SPC_PREVENT));
c->retry = 1;
c->dir = NO_TRANSFER;
d->issue_command(d, c);
#ifdef Libburn_pioneer_dvr_216d_get_evenT
mmc_get_event(d);
#endif
}
void spc_allow(struct burn_drive *d)
{
struct command *c;
c = &(d->casual_command);
if (mmc_function_spy(d, "allow") <= 0)
return;
scsi_init_command(c, SPC_ALLOW, sizeof(SPC_ALLOW));
c->retry = 1;
c->dir = NO_TRANSFER;
d->issue_command(d, c);
}
/* ts B40216 : Outsourced from spc_sense_caps_al().
To be called by spc_sense_caps() after spc_sense_caps_al()
*/
static int spc_try_get_performance(struct burn_drive *d, int flag)
{
int ret;
struct burn_feature_descr *feature_descr;
/* ts B40107 : Feature 0x107 announces availability of GET PERFORMANCE
Its WSPD bit announces Type 3.
Try this even if the feature is not current.
*/
ret = burn_drive_has_feature(d, 0x107, &feature_descr, 0);
if (ret <= 0)
return ret;
if (feature_descr->data_lenght <= 0)
return 1;
if (feature_descr->data[0] & 2) /* WSPD */
ret = mmc_get_write_performance(d);
/* Get read performance */
mmc_get_performance(d, 0x00, 0);
return 1;
}
/*
ts A70518 - A90603 : Do not call with *alloc_len < 10
*/
/** @param flag bit0= do only inquire alloc_len
@return 1=ok , <=0 error ,
2=Block Descriptor Length > 0, retry with flag bit1
*/
static int spc_sense_caps_al(struct burn_drive *d, int *alloc_len, int flag)
{
struct buffer *buf = NULL;
struct scsi_mode_data *m;
int page_length, num_write_speeds = 0, i, speed, ret;
int old_alloc_len, was_error = 0, block_descr_len;
unsigned char *page;
struct command *c = NULL;
struct burn_speed_descriptor *sd;
char *msg = NULL;
/* ts A61225 : 1 = report about post-MMC-1 speed descriptors */
static int speed_debug = 0;
if (*alloc_len < 10)
{ret = 0; goto ex;}
BURN_ALLOC_MEM(msg, char, BURN_DRIVE_ADR_LEN + 160);
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
/* ts A90602 : Clearing mdata before command execution */
m = d->mdata;
m->p2a_valid = 0;
burn_mdata_free_subs(m);
memset(buf, 0, sizeof(struct buffer));
scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
c->dxfer_len = *alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->opcode[2] = 0x2A;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error) {
memset(buf, 0, sizeof(struct buffer));
m->p2a_valid = -1;
was_error = 1;
}
/* ts B11103 : qemu SCSI CD-ROM has Block Descriptor Length > 0.
The descriptors come between header and page.
*/
block_descr_len = c->page->data[6] * 256 + c->page->data[7];
if (block_descr_len + 8 + 2 > *alloc_len) {
if (block_descr_len + 8 + 2 > BUFFER_SIZE || !(flag & 1)) {
m->p2a_valid = -1;
sprintf(msg,
"MODE SENSE page 2A with oversized Block Descriptors: %s : %d",
d->devname, block_descr_len);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002016e, LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_LOW, msg, 0, 0);
{ret = 0; goto ex;}
}
*alloc_len = block_descr_len + 10;
{ret = 2; goto ex;}
}
/* Skip over Mode Data Header and block descriptors */
page = c->page->data + 8 + block_descr_len;
/* ts A61225 :
Although MODE SENSE indeed belongs to SPC, the returned code page
2Ah is part of MMC-1 to MMC-3. In MMC-1 5.2.3.4. it has 22 bytes,
in MMC-3 6.3.11 there are at least 28 bytes plus a variable length
set of speed descriptors. In MMC-5 E.11 it is declared "legacy".
ts B11031 :
qemu emulates an ATAPI DVD-ROM, which delivers only a page length
of 18. This is now tolerated.
*/
/* ts A90603 :
SPC-1 8.3.3 enumerates mode page format bytes from 0 to n and
defines Page Length as (n-1).
*/
page_length = page[1];
old_alloc_len = *alloc_len;
*alloc_len = page_length + 10 + block_descr_len;
if (flag & 1)
{ret = !was_error; goto ex;}
if (page_length + 10 > old_alloc_len)
page_length = old_alloc_len - 10;
/* ts A90602 : page_length N asserts page[N+1]. (see SPC-1 8.3.3) */
/* ts B11031 : qemu drive has a page_length of 18 */
if (page_length < 18) {
m->p2a_valid = -1;
sprintf(msg, "MODE SENSE page 2A too short: %s : %d",
d->devname, page_length);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002016e, LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_LOW, msg, 0, 0);
{ret = 0; goto ex;}
}
m->buffer_size = page[12] * 256 + page[13];
m->dvdram_read = page[2] & 32;
m->dvdram_write = page[3] & 32;
m->dvdr_read = page[2] & 16;
m->dvdr_write = page[3] & 16;
m->dvdrom_read = page[2] & 8;
m->simulate = page[3] & 4;
m->cdrw_read = page[2] & 2;
m->cdrw_write = page[3] & 2;
m->cdr_read = page[2] & 1;
m->cdr_write = page[3] & 1;
m->c2_pointers = page[5] & 16;
m->underrun_proof = page[4] & 128;
/* ts A61021 : these fields are marked obsolete in MMC 3 */
m->max_read_speed = page[8] * 256 + page[9];
m->cur_read_speed = page[14] * 256 + page[15];
m->max_write_speed = m->cur_write_speed = 0;
if (page_length >= 18) /* note: page length is page size - 2 */
m->max_write_speed = page[18] * 256 + page[19];
if (page_length >= 20)
m->cur_write_speed = page[20] * 256 + page[21];
/* ts A61021 : New field to be set by atip (or following MMC-3 info) */
m->min_write_speed = m->max_write_speed;
/* ts A61225 : for ACh GET PERFORMANCE, Type 03h */
m->min_end_lba = 0x7fffffff;
m->max_end_lba = 0;
if (!was_error)
m->p2a_valid = 1;
/* ts A61225 : end of MMC-1 , begin of MMC-3 */
if (page_length < 30) /* no write speed descriptors ? */
goto no_speed_descriptors;
m->cur_write_speed = page[28] * 256 + page[29];
if (speed_debug)
fprintf(stderr, "LIBBURN_DEBUG: cur_write_speed = %d\n",
m->cur_write_speed);
num_write_speeds = page[30] * 256 + page[31];
m->max_write_speed = m->min_write_speed = m->cur_write_speed;
if (32 + 4 * num_write_speeds > page_length + 2) {
sprintf(msg, "Malformed capabilities page 2Ah received (len=%d, #speeds=%d)", page_length, num_write_speeds);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002013c,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
{ret = 0; goto ex;}
}
for (i = 0; i < num_write_speeds; i++) {
speed = page[32 + 4 * i + 2] * 256 + page[32 + 4 * i + 3];
if (speed_debug)
fprintf(stderr,
"LIBBURN_DEBUG: write speed #%d = %d kB/s (rc %d)\n",
i, speed, page[32 + 4 * i + 1] & 7);
/* ts A61226 */
ret = burn_speed_descriptor_new(&(d->mdata->speed_descriptors),
NULL, d->mdata->speed_descriptors, 0);
if (ret > 0) {
sd = d->mdata->speed_descriptors;
sd->source = 1;
if (d->current_profile > 0) {
sd->profile_loaded = d->current_profile;
strcpy(sd->profile_name,
d->current_profile_text);
}
sd->wrc = (( page[32 + 4 * i + 1] & 7 ) == 1 );
sd->write_speed = speed;
}
if (speed > m->max_write_speed)
m->max_write_speed = speed;
if (speed < m->min_write_speed)
m->min_write_speed = speed;
}
if (speed_debug)
fprintf(stderr,
"LIBBURN_DEBUG: 5Ah,2Ah min_write_speed = %d , max_write_speed = %d\n",
m->min_write_speed, m->max_write_speed);
no_speed_descriptors:;
ret = !was_error;
ex:
BURN_FREE_MEM(msg);
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
return ret;
}
void spc_sense_caps(struct burn_drive *d)
{
int alloc_len, start_len = 30, minimum_len = 28, ret;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "sense_caps") <= 0)
return;
mmc_get_configuration(d);
/* first command execution to learn Allocation Length */
alloc_len = start_len;
ret = spc_sense_caps_al(d, &alloc_len, 1);
if (ret == 2) {
/* ts B40205: Unexpectedly found Block Descriptors.
Repeat with new alloc_len.
*/
ret = spc_sense_caps_al(d, &alloc_len, 1);
if (ret == 2)
goto try_get_performance;
}
/* ts B11103:
qemu ATAPI DVD-ROM delivers only 28.
SanDisk Cruzer U3 memory stick throws error on alloc_len < 30.
MMC-1 prescribes that 30 are available. qemu tolerates 30.
*/
if (alloc_len >= minimum_len && ret > 0)
/* second execution with announced length */
spc_sense_caps_al(d, &alloc_len, 0);
try_get_performance:;
spc_try_get_performance(d, 0);
}
void spc_sense_error_params(struct burn_drive *d)
{
struct buffer *buf = NULL;
struct scsi_mode_data *m;
int alloc_len = 12 ;
unsigned char *page;
struct command *c = NULL;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "sense_error_params") <= 0)
goto ex;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
c->dxfer_len = alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->opcode[2] = 0x01;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
m = d->mdata;
page = c->page->data + 8;
d->params.retries = page[3];
m->retry_page_length = page[1];
m->retry_page_valid = 1;
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
}
void spc_select_error_params(struct burn_drive *d,
const struct burn_read_opts *o)
{
struct buffer *buf = NULL;
struct command *c = NULL;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "select_error_params") <= 0)
goto ex;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
c->retry = 1;
if (d->mdata->retry_page_valid <= 0)
d->mdata->retry_page_length = 0;
c->opcode[8] = 8 + 2 + d->mdata->retry_page_length;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
memset(c->page->data, 0, 8 + 2 + d->mdata->retry_page_length);
c->page->bytes = 8 + 2 + d->mdata->retry_page_length;
c->page->data[8] = 1;
c->page->data[9] = d->mdata->retry_page_length;
if (o->transfer_damaged_blocks)
c->page->data[10] |= 32;
if (o->report_recovered_errors)
c->page->data[10] |= 4;
if (!o->hardware_error_recovery)
c->page->data[10] |= 1;
c->page->data[11] = d->params.retries;
c->dir = TO_DRIVE;
d->issue_command(d, c);
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
}
void spc_sense_write_params(struct burn_drive *d)
{
struct buffer *buf = NULL;
struct scsi_mode_data *m;
int dummy1, dummy2, alloc_len = 10;
unsigned char *page;
struct command *c = NULL;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "sense_write_params") <= 0)
goto ex;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
/* ts A61007 : Done in soft at only caller burn_drive_grab() */
/* a ssert(d->mdata->cdr_write || d->mdata->cdrw_write ||
d->mdata->dvdr_write || d->mdata->dvdram_write); */
scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
c->dxfer_len = alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->opcode[2] = 0x05;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
/* ts A71128 : do not interpret reply if error */
m = d->mdata;
if (!c->error) {
page = c->page->data + 8;
m->write_page_length = page[1];
if (m->write_page_length > 0)
m->write_page_valid = 1;
else
m->write_page_length = 0x32;
}
mmc_read_disc_info(d);
/* ts A70212 : try to setup d->media_capacity_remaining */
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
d->current_profile == 0x12 || d->current_profile == 0x43)
d->read_format_capacities(d, -1);
else if (d->status == BURN_DISC_BLANK ||
(d->current_is_cd_profile && d->status == BURN_DISC_APPENDABLE)) {
burn_drive_send_default_page_05(d, 0);
d->get_nwa(d, -1, &dummy1, &dummy2);
}
/* others are hopefully up to date from mmc_read_disc_info() */
/*
fprintf(stderr, "LIBBURN_DEBUG: media_capacity_remaining = %.f\n",
(double) d->media_capacity_remaining);
*/
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
}
/* remark ts A61104 :
Although command MODE SELECT is SPC, the content of the
Write Parameters Mode Page (05h) is MMC (Table 108 in MMC-1).
Thus the filling of the mode page is done by mmc_compose_mode_page_5().
*/
void spc_select_write_params(struct burn_drive *d, struct burn_session *s,
int tnum, const struct burn_write_opts *o)
{
struct buffer *buf = NULL;
struct command *c = NULL;
int alloc_len;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "select_write_params") <= 0)
goto ex;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
/* ts A61007 : All current callers are safe. */
/* a ssert(o->drive == d); */
/* <<< A61030
fprintf(stderr,"libburn_debug: write_type=%d multi=%d control=%d\n",
o->write_type,o->multi,o->control);
fprintf(stderr,"libburn_debug: block_type=%d spc_block_type=%d\n",
o->block_type,spc_block_type(o->block_type));
*/
alloc_len = 8 + 2 + d->mdata->write_page_length;
memset(&(buf->data), 0, alloc_len);
#ifdef Libburn_pioneer_dvr_216d_load_mode5
scsi_init_command(c, SPC_MODE_SENSE, sizeof(SPC_MODE_SENSE));
c->dxfer_len = alloc_len;
c->opcode[7] = (alloc_len >> 8) & 0xff;
c->opcode[8] = alloc_len & 0xff;
c->retry = 1;
c->opcode[2] = 0x05;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error)
memset(&(buf->data), 0,
8 + 2 + d->mdata->write_page_length);
#endif /* Libburn_pioneer_dvr_216d_load_mode5 */
scsi_init_command(c, SPC_MODE_SELECT, sizeof(SPC_MODE_SELECT));
c->retry = 1;
c->opcode[7] = (alloc_len >> 8) & 0xff;
c->opcode[8] = alloc_len & 0xff;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->page->bytes = alloc_len;
/* ts A61229 */
if (mmc_compose_mode_page_5(d, s, tnum, o, c->page->data + 8) <= 0)
goto ex;
c->dir = TO_DRIVE;
d->issue_command(d, c);
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
}
#ifdef Libburn_enable_scsi_cmd_ABh
/* At least on Linux kernel 3.16 the command ABh causes EFAULT if not sent
from the superuser.
For a test it may be replaced by a dummy 28h READ12 on block 0.
This causes no EFAULT although it sets the wrong dxfer_len 4 rather
than 2048. So it is indeed a permission problem and not bad alignment.
*/
/* ts B51016 */
int spc_read_media_serial_number_al(struct burn_drive *d, int *alloc_len)
{
struct buffer *buf = NULL;
struct command *c = NULL;
unsigned char *data;
int ret;
if (*alloc_len < 4)
{ret = 0; goto ex;}
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
if (mmc_function_spy(d, "spc_read_media_serial_number") <= 0)
{ret = 0; goto ex;}
/*
#de fine Spc_read_media_serial_number_dummY yes
*/
#ifdef Spc_read_media_serial_number_dummY
{
static unsigned char MMC_READ_12[] =
{ 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
scsi_init_command(c, MMC_READ_12, sizeof(MMC_READ_12));
c->dxfer_len = *alloc_len;
}
#else
scsi_init_command(c, SPC_READ_MEDIA_SERIAL_NUMBER,
sizeof(SPC_READ_MEDIA_SERIAL_NUMBER));
c->dxfer_len = *alloc_len;
/* (Will not accept more than 32 KB anyway) */
c->opcode[8] = (c->dxfer_len >> 8) & 0xff;
c->opcode[9] = c->dxfer_len & 0xff;
#endif /* ! Spc_read_media_serial_number_dummY */
c->retry = 1;
c->page = buf;
memset(c->page->data, 0, *alloc_len);
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error)
{ret = 0; goto ex;}
data = c->page->data;
#ifdef Spc_read_media_serial_number_dummY
d->media_serial_number_len = 0;
#else
d->media_serial_number_len =
(data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[7];
#endif
if (*alloc_len >= d->media_serial_number_len + 4) {
if (d->media_serial_number != NULL)
BURN_FREE_MEM(d->media_serial_number);
BURN_ALLOC_MEM(d->media_serial_number, char,
d->media_serial_number_len + 1);
if (d->media_serial_number_len > 0)
memcpy(d->media_serial_number, data + 4,
d->media_serial_number_len);
d->media_serial_number[d->media_serial_number_len] = 0;
}
*alloc_len = d->media_serial_number_len + 4;
ret = 1;
ex:;
BURN_FREE_MEM(c);
BURN_FREE_MEM(buf);
return ret;
}
int spc_read_media_serial_number(struct burn_drive *d)
{
int alloc_len = 4, ret;
ret = spc_read_media_serial_number_al(d, &alloc_len);
if (alloc_len > 4 && alloc_len <= 0x8000 && ret > 0)
ret = spc_read_media_serial_number_al(d, &alloc_len);
return ret;
}
#endif /* Libburn_enable_scsi_cmd_ABh */
void spc_getcaps(struct burn_drive *d)
{
if (mmc_function_spy(d, "getcaps") <= 0)
return;
burn_speed_descriptor_destroy(&(d->mdata->speed_descriptors), 1);
spc_inquiry(d);
spc_sense_caps(d);
spc_sense_error_params(d);
}
/*
don't check totally stupid modes (raw/raw0)
some drives say they're ok, and they're not.
*/
void spc_probe_write_modes(struct burn_drive *d)
{
struct buffer *buf = NULL;
int try_write_type = 1;
int try_block_type = 0;
int key, asc, ascq, usable_write_type = -1, usable_block_type = -1;
int last_try = 0;
struct command *c = NULL;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "spc_probe_write_modes") <= 0)
goto ex;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
/* ts A70213 : added pseudo try_write_type 4 to set a suitable mode */
while (try_write_type != 5) {
/* ts A70213 */
if (try_write_type == 4) {
/* Pseudo write type NONE . Set a usable write mode */
if (usable_write_type == -1)
break;
try_write_type = usable_write_type;
try_block_type = usable_block_type;
last_try= 1;
}
scsi_init_command(c, SPC_MODE_SELECT,sizeof(SPC_MODE_SELECT));
c->retry = 1;
c->opcode[8] = 8 + 2 + 0x32;
c->page = buf;
memset(c->page->data, 0, 8 + 2 + 0x32);
c->page->bytes = 8 + 2 + 0x32;
c->page->data[8] = 5;
c->page->data[9] = 0x32;
c->page->data[10] = try_write_type;
if (try_block_type > 4)
c->page->data[11] = 4;
else
c->page->data[11] = 0;
c->page->data[12] = try_block_type;
c->page->data[23] = 150;
c->dir = TO_DRIVE;
d->silent_on_scsi_error = 1;
d->issue_command(d, c);
d->silent_on_scsi_error = 0;
if (last_try)
break;
spc_decode_sense(c->sense, 0, &key, &asc, &ascq);
if (key)
/* try_block_type not supported */;
else {
/* try_write_type, try_block_type is supported mode */
if (try_write_type == 2) /* sao */
d->block_types[try_write_type] =
BURN_BLOCK_SAO;
else
d->block_types[try_write_type] |=
1 << try_block_type;
/* ts A70213 */
if ((usable_write_type < 0 && try_write_type > 0) ||
(try_write_type == 1 && try_block_type == 8)) {
/* Packet is not supported yet.
Prefer TAO MODE_1. */
usable_write_type = try_write_type;
usable_block_type = try_block_type;
}
}
switch (try_block_type) {
case 0:
case 1:
case 2:
try_block_type++;
break;
case 3:
try_block_type = 8;
break;
case 8:
case 9:
case 10:
case 11:
case 12:
try_block_type++;
break;
case 13:
try_block_type = 0;
try_write_type++;
break;
default:
goto ex;
}
}
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
}
/* ( ts A61229 : shouldn't this go to mmc.c too ?) */
/** @return -1 = error */
int spc_block_type(enum burn_block_types b)
{
switch (b) {
case BURN_BLOCK_SAO:
return 0; /* ignored bitz */
case BURN_BLOCK_RAW0:
return 0;
case BURN_BLOCK_RAW16:
return 1;
case BURN_BLOCK_RAW96P:
return 2;
case BURN_BLOCK_RAW96R:
return 3;
case BURN_BLOCK_MODE1:
return 8;
case BURN_BLOCK_MODE2R:
return 9;
case BURN_BLOCK_MODE2_PATHETIC:
return 10;
case BURN_BLOCK_MODE2_LAME:
return 11;
case BURN_BLOCK_MODE2_OBSCURE:
return 12;
case BURN_BLOCK_MODE2_OK:
return 13;
default:
return -1;
}
/* ts A61007 : already prevented in burn_write_opts_set_write_type() */
/* a ssert(0); */;
}
/* ts A61021 : the spc specific part of sg.c:enumerate_common()
*/
int spc_setup_drive(struct burn_drive *d)
{
d->getcaps = spc_getcaps;
d->lock = spc_prevent;
d->unlock = spc_allow;
d->read_disc_info = spc_sense_write_params;
d->get_erase_progress = spc_get_erase_progress;
d->test_unit_ready = spc_test_unit_ready;
d->probe_write_modes = spc_probe_write_modes;
d->send_parameters = spc_select_error_params;
d->send_write_parameters = spc_select_write_params;
return 1;
}
/* ts A61021 : the general SCSI specific part of sg.c:enumerate_common()
@param flag Bitfiled for control purposes
bit0= do not setup spc/sbc/mmc
*/
int burn_scsi_setup_drive(struct burn_drive *d, int bus_no, int host_no,
int channel_no, int target_no, int lun_no, int flag)
{
int ret;
/* ts A60923 */
d->bus_no = bus_no;
d->host = host_no;
d->id = target_no;
d->channel = channel_no;
d->lun = lun_no;
/* ts A61106 */
d->silent_on_scsi_error = 0;
/* ts B21023 */
d->had_particular_error = 0;
d->idata = calloc(1, sizeof(struct burn_scsi_inquiry_data));
d->mdata = calloc(1, sizeof(struct scsi_mode_data));
/* ts A61007 : obsolete Assert in drive_getcaps() */
if (d->idata == NULL || d->mdata == NULL) {
libdax_msgs_submit(libdax_messenger, -1, 0x00020108,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
"Could not allocate new drive object", 0, 0);
return -1;
}
d->idata->valid = 0;
d->mdata->p2a_valid = 0;
d->mdata->max_read_speed = 0;
d->mdata->cur_read_speed = 0;
d->mdata->max_write_speed = 0;
d->mdata->cur_write_speed = 0;
d->mdata->speed_descriptors = NULL;
d->mdata->write_page_length = 0x32;
d->mdata->write_page_valid = 0;
if (!(flag & 1)) {
ret = spc_setup_drive(d);
if (ret<=0)
return ret;
ret = sbc_setup_drive(d);
if (ret<=0)
return ret;
ret = mmc_setup_drive(d);
if (ret<=0)
return ret;
}
return 1;
}
/* ts A61122 - A80829 */
enum response scsi_error_msg(struct burn_drive *d, unsigned char *sense,
int senselen, char msg_data[161],
int *key, int *asc, int *ascq)
{
int ret;
char *msg;
static char key_def[16][40] = {
"(no specific error)",
"Recovered error",
"Drive not ready",
"Medium error",
"Drive error",
"Illegal request",
"Drive event",
"Data protected",
"Blank/Nonblank",
"Vendor specific code",
"Copy aborted",
"Command aborted",
"(obsolete error code)",
"Volume overflow",
"Miscompare",
"(reserved error code)",
};
msg= msg_data;
*key= *asc= *ascq= -1;
ret = spc_decode_sense(sense, senselen, key, asc, ascq);
if (ret <= 0)
*key= *asc= *ascq= -1;
sprintf(msg, "[%X %2.2X %2.2X] ", *key, *asc, *ascq);
msg= msg + strlen(msg);
if (key_def[*key & 0xf][0] != '(') {
sprintf(msg, "%s. ", key_def[*key & 0xf]);
msg= msg + strlen(msg);
}
switch (*asc) {
case 0x00:
if (*key > 0 || *ascq > 0)
break; /* Fall through to unknown error */
sprintf(msg, "(No error reported by SCSI transaction)");
return GO_ON;
case 0x02:
sprintf(msg, "Not ready");
goto return_retry;
case 0x04:
if (*ascq == 1)
sprintf(msg,
"Logical unit is in the process of becoming ready");
else
sprintf(msg, "Logical unit is not ready");
goto return_retry;
case 0x06:
if (*ascq == 0)
sprintf(msg, "No reference position found");
else
break;
goto return_fail;
case 0x08:
if (*ascq == 0)
sprintf(msg, "Logical unit communication failure");
else if (*ascq == 1)
sprintf(msg, "Logical unit communication timeout");
else if (*ascq == 2)
sprintf(msg, "Logical unit communication parity error");
else if (*ascq == 3)
sprintf(msg, "Logical unit communication crc error");
else
break;
goto return_retry;
case 0x09:
if (*ascq == 0)
sprintf(msg, "Track following error");
else if (*ascq == 1)
sprintf(msg, "Tracking servo failure");
else if (*ascq == 2)
sprintf(msg, "Focus servo failure");
else if (*ascq == 3)
sprintf(msg, "Spindle servo failure");
else if (*ascq == 4)
sprintf(msg, "Head select fault");
else
break;
goto return_fail;
case 0x0C:
if (*ascq == 0)
sprintf(msg, "Write error");
else if (*ascq == 1)
sprintf(msg,
"Write error, recovered with auto-allocation");
else if (*ascq == 2)
sprintf(msg, "Write error, auto reallocation failed");
else if (*ascq == 7)
sprintf(msg, "Write error, recovery needed");
else if (*ascq == 8)
sprintf(msg, "Write error, recovery failed");
else if (*ascq == 9)
sprintf(msg, "Write error, loss of streaming");
else if (*ascq == 0x0f)
sprintf(msg, "Defects in error window");
else
break;
goto return_fail;
case 0x11:
if (*ascq == 0)
sprintf(msg, "Unrecovered read error");
else if (*ascq == 1)
sprintf(msg, "Read retries exhausted");
else if (*ascq == 2)
sprintf(msg, "Error too long to correct");
else if (*ascq == 5)
sprintf(msg, "L-EC uncorrectable error");
else if (*ascq == 6)
sprintf(msg, "CIRC uncorrectable error");
else
break;
goto return_fail;
case 0x15:
if (*ascq == 0)
sprintf(msg, "Random positioning error");
else if (*ascq == 1)
sprintf(msg, "Mechanical positioning error");
else
break;
goto return_fail;
case 0x1a:
if (*ascq == 0)
sprintf(msg, "Parameter list length error");
else
break;
goto return_fail;
case 0x1b:
if (*ascq == 0)
sprintf(msg, "Synchronous data transfer error");
else
break;
goto return_fail;
case 0x20:
if (*ascq == 0)
sprintf(msg, "Invalid command operation code");
else
break;
goto return_fail;
case 0x21:
if (*ascq == 0)
sprintf(msg, "Lba out of range");
else if (*ascq == 1)
sprintf(msg, "Invalid element address");
else if (*ascq == 2)
sprintf(msg, "Invalid address for write");
else if (*ascq == 3)
sprintf(msg, "Invalid write crossing layer jump");
else
break;
goto return_fail;
case 0x24:
if (*ascq == 0)
sprintf(msg, "Invalid field in cdb");
else
break;
goto return_fail;
case 0x26:
if (*ascq == 0)
sprintf(msg, "Invalid field in parameter list");
else if (*ascq == 1)
sprintf(msg, "Parameter not supported");
else if (*ascq == 2)
sprintf(msg, "Parameter value invalid");
else
break;
goto return_fail;
case 0x27:
sprintf(msg, "Write protected");
goto return_fail;
case 0x28:
if (*ascq == 0)
sprintf(msg, "Medium may have changed");
else if (*ascq == 1)
sprintf(msg, "Import or export element accessed");
else if (*ascq == 2)
sprintf(msg, "Format layer may have changed");
else if (*ascq == 3)
sprintf(msg,
"Import/export element accessed, medium changed");
else if (*key == 6)
sprintf(msg, "Unknown ASCQ with drive event ASC 28");
else
break;
goto return_retry;
case 0x29:
if (*ascq == 0)
sprintf(msg,
"Power on, reset, or bus device reset occurred");
else if (*ascq == 1)
sprintf(msg, "Power on occurred");
else if (*ascq == 2)
sprintf(msg, "Bus reset occurred");
else if (*ascq == 3)
sprintf(msg, "Bus device reset function occurred");
else if (*ascq == 4)
sprintf(msg, "Device internal reset");
else
break;
goto return_retry;
case 0x2c:
if (*ascq == 0)
sprintf(msg, "Command sequence error");
else
break;
goto return_fail;
case 0x2e:
if (*ascq == 0)
sprintf(msg, "Insufficient time for operation");
else
break;
goto return_fail;
case 0x30:
if (*ascq == 0)
sprintf(msg, "Incompatible medium installed");
else if (*ascq == 1)
sprintf(msg, "Cannot read medium, unknown format");
else if (*ascq == 2)
sprintf(msg,
"Cannot read medium, incompatible format");
else if (*ascq == 4)
sprintf(msg, "Cannot write medium, unknown format");
else if (*ascq == 5)
sprintf(msg,
"Cannot write medium, incompatible format");
else if (*ascq == 6)
sprintf(msg,
"Cannot format medium, incompatible medium");
else if (*ascq == 7)
sprintf(msg, "Cleaning failure");
else if (*ascq == 8)
sprintf(msg,
"Cannot write, application code mismatch");
else if (*ascq == 9)
sprintf(msg, "Current session not fixated for append");
else if (*ascq == 10)
sprintf(msg, "Medium not formatted");
else if (*ascq == 11)
sprintf(msg,
"Cannot write medium, unsupported medium version");
else
break;
goto return_fail;
case 0x31:
if (*ascq == 0)
sprintf(msg, "Medium unformatted or format corrupted");
else if (*ascq == 1)
sprintf(msg, "Format command failed");
else
break;
goto return_fail;
case 0x32:
if (*ascq == 0)
sprintf(msg, "No defect spare location available");
else
break;
goto return_fail;
case 0x3A:
if (*ascq == 0)
sprintf(msg, "Medium not present");
else if (*ascq == 1)
sprintf(msg, "Medium not present, tray closed");
else if (*ascq == 2)
sprintf(msg, "Medium not present, tray open");
else if (*ascq == 3)
sprintf(msg, "Medium not present, loadable");
else
break;
d->status = BURN_DISC_EMPTY;
goto return_fail;
case 0x3E:
if (*ascq == 1)
sprintf(msg, "Logical unit failure");
else if (*ascq == 2)
sprintf(msg, "Timeout on logical unit");
else
break;
goto return_fail;
case 0x44:
if (*ascq == 0)
sprintf(msg, "Internal target failure");
else
break;
goto return_fail;
case 0x51:
if (*ascq == 0)
sprintf(msg, "Erase failure");
else if (*ascq == 1)
sprintf(msg, "Erase failure. Incomplete erase operation");
else
break;
goto return_fail;
case 0x57:
if (*ascq == 0)
sprintf(msg, "Unable to recover Table-of-Content");
else
break;
goto return_fail;
case 0x63:
if (*ascq == 0)
sprintf(msg,
"End of user area encountered on this track");
else if (*ascq == 1)
sprintf(msg, "Packet does not fit in available space");
else
break;
goto return_fail;
case 0x64:
if (*ascq == 0)
sprintf(msg, "Illegal mode for this track");
else if (*ascq == 1)
sprintf(msg, "Invalid packet size");
else
break;
goto return_fail;
case 0x6F:
if (*ascq == 0)
sprintf(msg, "Copy protection key exchange failure Authentication failure");
else if (*ascq == 1)
sprintf(msg, "Copy protection key exchange failure Key not present");
else if (*ascq == 2)
sprintf(msg, "Copy protection key exchange failure Key not established");
else if (*ascq == 3)
sprintf(msg, "Read of scrambled sector without authentication");
else if (*ascq == 4)
sprintf(msg, "Media region code is mismatched to logical unit region");
else if (*ascq == 5)
sprintf(msg, "Logical unit region must be permanent / Region reset count error");
else if (*ascq == 6)
sprintf(msg, "Insufficient block count for binding nonce recording");
else if (*ascq == 7)
sprintf(msg, "Conflict in binding nonce recording");
else if (*ascq == 8)
sprintf(msg, "Insufficient permission");
else
break;
goto return_fail;
case 0x72:
if (*ascq == 0)
sprintf(msg, "Session fixation error");
else if (*ascq == 1)
sprintf(msg, "Session fixation error writing lead-in");
else if (*ascq == 2)
sprintf(msg,
"Session fixation error writing lead-out");
else if (*ascq == 3)
sprintf(msg,
"Session fixation error, incomplete track in session");
else if (*ascq == 4)
sprintf(msg,
"Empty or partially written reserved track");
else if (*ascq == 5)
sprintf(msg, "No more track reservations allowed");
else
break;
goto return_fail;
case 0x73:
if (*ascq == 0)
sprintf(msg, "CD control error");
else if (*ascq == 1)
sprintf(msg, "Power calibration area almost full");
else if (*ascq == 2)
sprintf(msg, "Power calibration area is full");
else if (*ascq == 3)
sprintf(msg, "Power calibration area error");
else if (*ascq == 4)
sprintf(msg, "Program memory area update failure");
else if (*ascq == 5)
sprintf(msg, "Program memory area is full");
else
break;
goto return_fail;
}
sprintf(msg_data,
"See MMC specs: Sense Key %X \"%s\", ASC %2.2X ASCQ %2.2X",
*key & 0xf, key_def[(*key) & 0xf], *asc, *ascq);
goto return_fail;
return_fail:
strcat(msg, ".");
if (*key == 1)
return GO_ON;
return FAIL;
return_retry:;
strcat(msg, ".");
if (*key == 1)
return GO_ON;
return RETRY;
}
/* ts A61115 moved from sg-*.c */
/* ts A61122 made it frontend to scsi_error_msg() */
enum response scsi_error(struct burn_drive *d, unsigned char *sense,
int senselen)
{
int key, asc, ascq, ret = 0;
char *msg = NULL;
enum response resp;
BURN_ALLOC_MEM(msg, char, 160);
resp = scsi_error_msg(d, sense, senselen, msg, &key, &asc, &ascq);
ex:;
if (ret == -1)
resp = FAIL;
BURN_FREE_MEM(msg);
return resp;
}
static char *scsi_command_name(unsigned int c, int flag)
{
switch (c) {
case 0x00:
return "TEST UNIT READY";
case 0x03:
return "REQUEST SENSE";
case 0x04:
return "FORMAT UNIT";
case 0x1b:
return "START/STOP UNIT";
case 0x12:
return "INQUIRY";
case 0x1e:
return "PREVENT/ALLOW MEDIA REMOVAL";
case 0x23:
return "READ FORMAT CAPACITIES";
case 0x25:
return "READ CAPACITY";
case 0x28:
return "READ(10)";
case 0x2a:
return "WRITE(10)";
case 0x35:
return "SYNCHRONIZE CACHE";
case 0x43:
return "READ TOC/PMA/ATIP";
case 0x46:
return "GET CONFIGURATION";
case 0x4a:
return "GET EVENT STATUS NOTIFICATION";
case 0x51:
return "READ DISC INFORMATION";
case 0x52:
return "READ TRACK INFORMATION";
case 0x53:
return "RESERVE TRACK";
case 0x54:
return "SEND OPC INFORMATION";
case 0x55:
return "MODE SELECT";
case 0x5a:
return "MODE SENSE";
case 0x5b:
return "CLOSE TRACK/SESSION";
case 0x5c:
return "READ BUFFER CAPACITY";
case 0x5d:
return "SEND CUE SHEET";
case 0xa1:
return "BLANK";
case 0xaa:
return "WRITE(12)";
case 0xab:
return "READ MEDIA SERIAL NUMBER";
case 0xac:
return "GET PERFORMANCE";
case 0xad:
return "READ DISC STRUCTURE";
case 0xb6:
return "SET STREAMING";
case 0xb9:
return "READ CD MSF";
case 0xbb:
return "SET CD SPEED";
case 0xbe:
return "READ CD";
#ifdef Libburn_develop_quality_scaN
case 0xf3:
return "NEC/OPTIARC REPORT ERROR RATE";
#endif /* Libburn_develop_quality_scaN */
}
return "(NOT IN LIBBURN COMMAND LIST)";
}
/* ts B90206: Avoid publishing more inner API functions which begin by scsi_ */
char *spc_command_name(unsigned int c, int flag)
{
return(scsi_command_name(c, flag));
}
/* ts B90616 */
void spc_register_retry(struct command *c)
{
c->retry_count++;
spc_decode_sense(c->sense, c->sense_len, &c->last_retry_key,
&c->last_retry_asc, &c->last_retry_ascq);
}
/* ts B90511 */
/* @param flag bit0= do not prepend command name
bit1= do not append dxfer_len
*/
int spc_human_readable_cmd(struct command *c, char *msg, int msg_max, int flag)
{
int j, l, lname;
if ((flag & 1) && c->retry_count <= 0) {
msg[0] = 0;
} else {
if (msg_max < 60)
return -1;
strcpy(msg, spc_command_name( (unsigned int) c->opcode[0], 0));
if (c->retry_count > 0) {
sprintf(msg + strlen(msg), " #%d", c->retry_count + 1);
if (c->last_retry_key > 0)
sprintf(msg + strlen(msg), ",[%X %2.2X %2.2X]",
c->last_retry_key, c->last_retry_asc,
c->last_retry_ascq);
}
strcat(msg, " : ");
}
lname = l = strlen(msg);
for (j = 0; j < 16 && j < c->oplen; j++) {
if (l > msg_max - 3) {
if (msg_max - 4 >= lname) {
l = msg_max - 4;
sprintf(msg + strlen(msg), "... ");
}
return 0;
}
sprintf(msg + l, "%2.2x ", c->opcode[j]);
l += 3;
}
if (c->dir != NO_TRANSFER && c->page != NULL && !(flag & 2)) {
if (l > msg_max - 24)
return 0;
sprintf(msg + l, " : dxfer_len= %d", c->dxfer_len);
l = strlen(msg);
}
return 1;
}
/* ts A61030 - A61115 */
/* @param flag bit0= do report conditions which are considered not an error
bit1= report with severity FAILURE rather than DEBUG
*/
int scsi_notify_error(struct burn_drive *d, struct command *c,
unsigned char *sense, int senselen, int flag)
{
int key= -1, asc= -1, ascq= -1, ret;
char *msg = NULL, *scsi_msg = NULL;
if (d->silent_on_scsi_error == 1 || d->silent_on_scsi_error == 2)
{ret = 1; goto ex;}
BURN_ALLOC_MEM(msg, char, 320);
BURN_ALLOC_MEM(scsi_msg, char, 160);
scsi_error_msg(d, sense, senselen, scsi_msg, &key, &asc, &ascq);
if (!(flag & 1)) {
/* SPC : TEST UNIT READY command */
if (c->opcode[0] == 0)
{ret = 1; goto ex;}
/* MMC : READ DISC INFORMATION command */
if (c->opcode[0] == 0x51)
if (key == 0x2 && asc == 0x3A &&
ascq>=0 && ascq <= 0x02) /* MEDIUM NOT PRESENT */
{ret = 1; goto ex;}
if (key == 0 && asc == 0 && ascq == 0)
{ret = 1; goto ex;}
}
sprintf(msg, "SCSI error condition on command %2.2Xh %s: ",
c->opcode[0],
scsi_command_name((unsigned int) c->opcode[0], 0));
strcat(msg, scsi_msg);
ret = libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010f,
(flag & 2) && d->silent_on_scsi_error != 3 ?
LIBDAX_MSGS_SEV_FAILURE : LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
strcpy(msg, "CDB= ");
if (spc_human_readable_cmd(c, msg + strlen(msg), 320 - strlen(msg), 1)
> 0) {
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002010f,
(flag & 2) && d->silent_on_scsi_error != 3 ?
LIBDAX_MSGS_SEV_FAILURE : LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
}
ex:;
BURN_FREE_MEM(msg);
BURN_FREE_MEM(scsi_msg);
return ret;
}
/* ts B11110 */
/* @param flag bit0= do not show eventual data payload sent to the drive
(never with WRITE commands)
*/
int scsi_show_command(unsigned char *opcode, int oplen, int dir,
unsigned char *data, int bytes,
void *fp_in, int flag)
{
int i;
FILE *fp = fp_in;
fprintf(fp, "\n%s\n",
scsi_command_name((unsigned int) opcode[0], 0));
for(i = 0; i < 16 && i < oplen; i++)
fprintf(fp, "%2.2x ", opcode[i]);
if (i > 0)
fprintf(fp, "\n");
if (flag & 1)
return 1;
if (opcode[0] == 0x2A) { /* WRITE 10 */
if ((flag & 2) && oplen > 8)
fprintf(fp, "%d -> %d\n",
(opcode[7] << 8) | opcode[8],
mmc_four_char_to_int(opcode + 2));
} else if (opcode[0] == 0xAA) { /* WRITE 12 */
if ((flag & 2) && oplen > 9)
fprintf(fp, "%d -> %d\n",
mmc_four_char_to_int(opcode + 6),
mmc_four_char_to_int(opcode + 2));
} else if (dir == TO_DRIVE && !(flag & 1)) {
fprintf(fp, "To drive: %db\n", bytes);
for (i = 0; i < bytes; i++)
fprintf(fp, "%2.2x%c", data[i],
((i % 20) == 19 ? '\n' : ' '));
if (i % 20)
fprintf(fp, "\n");
}
return 1;
}
/* ts A91106 */
/* @param flag bit0= do not show eventual data payload sent to the drive
(never with WRITE commands)
*/
int scsi_show_cmd_text(struct command *c, void *fp_in, int flag)
{
return scsi_show_command(c->opcode, c->oplen, c->dir, c->page->data,
c->page->bytes, fp_in, flag);
}
/* ts A91106 */ /* ts B11110 */
int scsi_show_command_reply(unsigned char *opcode, int data_dir,
unsigned char *data, int dxfer_len,
void *fp_in, int flag)
{
int i;
FILE *fp = fp_in;
if (data_dir != FROM_DRIVE)
return 2;
if (opcode[0] == 0x28 || opcode[0] == 0x3C ||
opcode[0] == 0xA8 || opcode[0] == 0xB9 || opcode[0] == 0xBE) {
/* READ commands */
/* >>> report amount of data */;
return 2;
}
fprintf(fp, "From drive: %db\n", dxfer_len);
for (i = 0; i < dxfer_len; i++)
fprintf(fp, "%2.2x%c", data[i],
((i % 20) == 19 ? '\n' : ' '));
if (i % 20)
fprintf(fp, "\n");
return 1;
}
/* ts B11110 */
/** Logs command (before execution) */
int scsi_log_command(unsigned char *opcode, int oplen, int data_dir,
unsigned char *data, int bytes,
void *fp_in, int flag)
{
FILE *fp = fp_in;
if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) {
scsi_show_command(opcode, oplen, data_dir, data, bytes, fp, 0);
if (burn_sg_log_scsi & 4)
fflush(fp);
}
if (fp == stderr || !(burn_sg_log_scsi & 2))
return 1;
scsi_log_command(opcode, oplen, data_dir, data, bytes, stderr, 0);
return 1;
}
/* ts B40731 */
/* Arbitrary SCSI log message */
int scsi_log_text(char *text, void *fp_in, int flag)
{
FILE *fp = fp_in;
if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) {
fprintf(fp, "%s\n", text);
if (burn_sg_log_scsi & 4)
fflush(fp);
}
if (fp == stderr || !(burn_sg_log_scsi & 2))
return 1;
fprintf(stderr, "%s\n", text);
return 1;
}
/* ts A91218 (former sg_log_cmd ts A70518) */
/** Logs command (before execution) */
int scsi_log_cmd(struct command *c, void *fp_in, int flag)
{
int ret, bytes = 0;
unsigned char *data = NULL;
if (c->page != NULL) {
data = c->page->data;
bytes = c->page->bytes;
}
ret = scsi_log_command(c->opcode, c->oplen, c->dir, data, bytes,
fp_in, flag);
return ret;
}
/* ts B11110 */
/** Logs outcome of a sg command.
@param flag bit0 causes an error message
bit1 do not print duration
*/
int scsi_log_reply(unsigned char *opcode, int data_dir, unsigned char *data,
int dxfer_len, void *fp_in, unsigned char sense[18],
int sense_len, double duration, int flag)
{
FILE *fp = fp_in;
int key, asc, ascq, i, l;
if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) {
if (flag & 1) {
l = 18;
if ((sense[0] & 0x7f) == 0x72 ||
(sense[0] & 0x7f) == 0x73)
l = sense[7] + 7 + 1; /* SPC-3 4.5.2. */
if (l > sense_len)
l = sense_len;
fprintf(fp, "+++ sense data =");
for (i = 0 ; i < l; i++)
fprintf(fp, " %2.2X", sense[i]);
fprintf(fp, "\n");
spc_decode_sense(sense, 0, &key, &asc, &ascq);
fprintf(fp, "+++ key=%X asc=%2.2Xh ascq=%2.2Xh\n",
(unsigned int) key, (unsigned int) asc,
(unsigned int) ascq);
} else {
scsi_show_command_reply(opcode, data_dir, data,
dxfer_len, fp, 0);
}
if (!(flag & 2))
fprintf(fp, " %8.f us [ %.f ]\n",
duration * 1.0e6,
(burn_get_time(0) - lib_start_time) * 1.0e6);
if (burn_sg_log_scsi & 4)
fflush(fp);
}
if (fp == stderr || !(burn_sg_log_scsi & 2))
return 1;
scsi_log_reply(opcode, data_dir, data, dxfer_len,
stderr, sense, sense_len, duration, flag);
return 1;
}
/* ts A91221 (former sg_log_err ts A91108) */
/** Legacy frontend to scsi_log_reply().
@param flag bit0 causes an error message
bit1 do not print duration
*/
int scsi_log_err(struct burn_drive *d, struct command *c,
void *fp_in, unsigned char sense[18],
int sense_len, int flag)
{
int ret;
unsigned char *data = NULL;
if (c->page != NULL)
data = c->page->data;
ret= scsi_log_reply(c->opcode, c->dir, data, c->dxfer_len ,
fp_in, sense, sense_len,
c->end_time - c->start_time, flag);
return ret;
}
/* ts B31112 */
int scsi_log_message(struct burn_drive *d, void *fp_in, char * msg, int flag)
{
int ret;
FILE *fp = fp_in;
if (fp != NULL && (fp == stderr || (burn_sg_log_scsi & 1))) {
fprintf(fp, "%s\n", msg);
if (burn_sg_log_scsi & 4)
fflush(fp);
}
if (fp == stderr || !(burn_sg_log_scsi & 2))
return 1;
ret = scsi_log_message(d, stderr, msg, flag);
return ret;
}
/* ts B00808 */
/*
@param flag bit0 = do not retry
bit1 = do not print duration
@return 0 = not yet done , 1 = done , -1 = error
*/
int scsi_eval_cmd_outcome(struct burn_drive *d, struct command *c, void *fp,
unsigned char *sense, int sense_len,
time_t start_time, int timeout_ms,
int loop_count, int flag)
{
enum response outcome;
int done = -1, usleep_time;
char *msg = NULL;
if (burn_sg_log_scsi & 3)
scsi_log_err(d, c, fp, sense, sense_len,
(sense_len > 0) | (flag & 2));
if (sense == c->sense)
c->sense_len = sense_len;
if (sense_len <= 0)
{done = 1; goto ex;}
outcome = scsi_error(d, sense, sense_len);
if (outcome == RETRY && c->retry && !(flag & 1)) {
/* Calming down retries and breaking up endless cycle
*/
if (c->opcode[0] == 0x2A || c->opcode[0] == 0xAA) {
/* WRITE(10) , WRITE(12) */
usleep_time = Libburn_scsi_write_retry_usleeP +
loop_count * Libburn_scsi_write_retry_incR;
if (usleep_time > Libburn_scsi_write_retry_umaX)
usleep_time = Libburn_scsi_write_retry_umaX;
} else {
usleep_time = Libburn_scsi_retry_usleeP +
loop_count * Libburn_scsi_retry_incR;
if (usleep_time > Libburn_scsi_retry_umaX)
usleep_time = Libburn_scsi_retry_umaX;
}
if (time(NULL) + usleep_time / 1000000 - start_time >
timeout_ms / 1000 + 1) {
done = -1; /* In case of alloc failure */
BURN_ALLOC_MEM_VOID(msg, char, 320);
done = 1;
sprintf(msg,
"Timeout exceed (%d ms). Retry canceled.\n",
timeout_ms);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002018a,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
strcpy(msg, "Command: ");
if (spc_human_readable_cmd(c, msg + strlen(msg),
320 - strlen(msg), 0) > 0)
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x0002018a,
LIBDAX_MSGS_SEV_SORRY,
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
goto err_ex;
}
if (d->cancel)
{done = 1; goto ex;}
if (usleep_time > 0)
usleep(usleep_time);
if (d->cancel)
{done = 1; goto ex;}
if (burn_sg_log_scsi & 3)
scsi_log_cmd(c, fp, 0);
{done = 0; goto ex;}
} else if (outcome == RETRY) {
done = 1;
} else if (outcome == GO_ON) {
{done = 1; goto ex;}
} else if (outcome == FAIL) {
done = 1;
}
err_ex:;
c->error = 1;
scsi_notify_error(d, c, sense, sense_len, 0);
ex:;
BURN_FREE_MEM(msg);
return done;
}
int spc_confirm_cd_drive(struct burn_drive *d, int flag)
{
char *msg = NULL;
int ret;
BURN_ALLOC_MEM(msg, char, strlen(d->devname) + 1024);
spc_inquiry(d);
if (d->idata->valid < 0) {
sprintf(msg, "INQUIRY failed with drive '%s'", d->devname);
libdax_msgs_submit(libdax_messenger, -1, 0x0002000a,
LIBDAX_MSGS_SEV_FAILURE,
LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
ret = 0; goto ex;
}
if (d->idata->peripheral != 0x5) {
sprintf(msg, "Does not identify itself as CD-ROM drive '%s'",
d->devname);
libdax_msgs_submit(libdax_messenger, -1, 0x0002000a,
LIBDAX_MSGS_SEV_FAILURE,
LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
ret = 0; goto ex;
}
ret = 1;
ex:;
BURN_FREE_MEM(msg);
return ret;
}