|
|
|
@ -87,6 +87,14 @@ Send feedback to libburn-hackers@pykix.org .
|
|
|
|
|
#include <cdio/mmc.h>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The waiting time before eventually retrying a failed SCSI command.
|
|
|
|
|
Before each retry wait Libburn_sg_linux_retry_incR longer than with
|
|
|
|
|
the previous one.
|
|
|
|
|
*/
|
|
|
|
|
#define Libburn_sg_libcdio_retry_usleeP 100000
|
|
|
|
|
#define Libburn_sg_libcdio_retry_incR 100000
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** PORTING : ------ libburn portable headers and definitions ----- */
|
|
|
|
|
|
|
|
|
|
#include "transport.h"
|
|
|
|
@ -117,6 +125,18 @@ int burn_drive_is_banned(char *device_address);
|
|
|
|
|
extern int burn_sg_log_scsi;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* PORTING: Private definitions. Port only if needed by public functions. */
|
|
|
|
|
/* (Public functions are listed below) */
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Storage object is in libburn/init.c
|
|
|
|
|
whether to strive for exclusive access to the drive
|
|
|
|
|
*/
|
|
|
|
|
extern int burn_sg_open_o_excl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
|
/* PORTING: Private functions. Port only if needed by public functions */
|
|
|
|
|
/* (Public functions are listed below) */
|
|
|
|
@ -232,7 +252,8 @@ int sg_give_next_adr(burn_drive_enumerator_t *idx,
|
|
|
|
|
int scsi_enumerate_drives(void)
|
|
|
|
|
{
|
|
|
|
|
burn_drive_enumerator_t idx;
|
|
|
|
|
int initialize = 1, ret;
|
|
|
|
|
int initialize = 1, ret, i_bus_no = -1;
|
|
|
|
|
int i_host_no = -1, i_channel_no = -1, i_target_no = -1, i_lun_no = -1;
|
|
|
|
|
char buf[64];
|
|
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
@ -242,10 +263,10 @@ int scsi_enumerate_drives(void)
|
|
|
|
|
break;
|
|
|
|
|
if (burn_drive_is_banned(buf))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* >>> try to obtain bus,host,channel,target,lun */;
|
|
|
|
|
|
|
|
|
|
enumerate_common(buf, -1, -1, -1, -1, -1);
|
|
|
|
|
sg_obtain_scsi_adr(buf, &i_bus_no, &i_host_no,
|
|
|
|
|
&i_channel_no, &i_target_no, &i_lun_no);
|
|
|
|
|
enumerate_common(buf, i_bus_no, i_host_no, i_channel_no,
|
|
|
|
|
i_target_no, i_lun_no);
|
|
|
|
|
}
|
|
|
|
|
sg_give_next_adr(&idx, buf, sizeof(buf), -1);
|
|
|
|
|
return 1;
|
|
|
|
@ -272,12 +293,44 @@ int sg_drive_is_open(struct burn_drive * d)
|
|
|
|
|
int sg_grab(struct burn_drive *d)
|
|
|
|
|
{
|
|
|
|
|
CdIo_t *p_cdio;
|
|
|
|
|
char *am, *version_text;
|
|
|
|
|
char msg[160];
|
|
|
|
|
int cdio_ver = 82;
|
|
|
|
|
|
|
|
|
|
if(d->p_cdio != NULL) {
|
|
|
|
|
if (d->p_cdio != NULL) {
|
|
|
|
|
d->released = 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
p_cdio = cdio_open(d->devname, DRIVER_DEVICE);
|
|
|
|
|
|
|
|
|
|
sprintf(msg, "Using sg-libcdio-%d with libcdio version ",
|
|
|
|
|
LIBCDIO_VERSION_NUM );
|
|
|
|
|
|
|
|
|
|
/* >>> change this to #if LIBCDIO_VERSION_NUM < 83 */
|
|
|
|
|
#ifdef LIBCDIO_HAS_VERSION_CALL
|
|
|
|
|
cdio_ver = cdio_version(&version_text);
|
|
|
|
|
#else
|
|
|
|
|
LIBBURN_MISCONFIGURATION = 0;
|
|
|
|
|
INTENTIONAL_ABORT_OF_COMPILATION__HEADERFILE_cdio_version_dot_h_TOO_OLD__NEED_LIBCDIO_HAS_VERSION_CALL = 0;
|
|
|
|
|
LIBBURN_MISCONFIGURATION_ = 0;
|
|
|
|
|
#endif /* ! LIBCDIO_HAS_VERSION_CALL */
|
|
|
|
|
|
|
|
|
|
strncat(msg, version_text, 80);
|
|
|
|
|
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
|
|
|
|
|
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
|
|
|
|
msg , 0, 0);
|
|
|
|
|
if (cdio_ver < LIBCDIO_VERSION_NUM) {
|
|
|
|
|
sprintf(msg,
|
|
|
|
|
"libcdio TOO OLD: numeric version %d , need at least %d",
|
|
|
|
|
cdio_ver, LIBCDIO_VERSION_NUM);
|
|
|
|
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
|
|
|
|
0x00020003,
|
|
|
|
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
|
|
|
|
msg, 0, 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p_cdio = cdio_open_am(d->devname, DRIVER_DEVICE,
|
|
|
|
|
burn_sg_open_o_excl ? "MMC_RDWR_EXCL" : "MMC_RDWR");
|
|
|
|
|
|
|
|
|
|
if (p_cdio == NULL) {
|
|
|
|
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
|
|
|
@ -286,6 +339,16 @@ int sg_grab(struct burn_drive *d)
|
|
|
|
|
"Could not grab drive", 0/*os_errno*/, 0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
am = (char *) cdio_get_arg(p_cdio, "access-mode");
|
|
|
|
|
if (strncmp(am, "MMC_RDWR", 8) != 0) {
|
|
|
|
|
libdax_msgs_submit(libdax_messenger, d->global_index,
|
|
|
|
|
0x00020003,
|
|
|
|
|
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
|
|
|
|
|
"libcdio provides no MMC_RDWR access mode", 0, 0);
|
|
|
|
|
cdio_destroy(p_cdio);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d->p_cdio = p_cdio;
|
|
|
|
|
d->released = 0;
|
|
|
|
|
return 1;
|
|
|
|
@ -320,13 +383,15 @@ int sg_release(struct burn_drive *d)
|
|
|
|
|
*/
|
|
|
|
|
int sg_issue_command(struct burn_drive *d, struct command *c)
|
|
|
|
|
{
|
|
|
|
|
int i_status;
|
|
|
|
|
int sense_valid = 0, i, usleep_time, timeout_ms;
|
|
|
|
|
time_t start_time;
|
|
|
|
|
driver_return_code_t i_status;
|
|
|
|
|
unsigned int dxfer_len;
|
|
|
|
|
static FILE *fp = NULL;
|
|
|
|
|
mmc_cdb_t cdb = {{0, }};
|
|
|
|
|
cdio_mmc_direction_t e_direction;
|
|
|
|
|
CdIo_t *p_cdio;
|
|
|
|
|
char msg[160];
|
|
|
|
|
unsigned char sense[18], *sense_pt = NULL;
|
|
|
|
|
|
|
|
|
|
c->error = 0;
|
|
|
|
|
if (d->p_cdio == NULL) {
|
|
|
|
@ -357,31 +422,35 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
|
|
|
|
|
memset(c->page->data, 0, BUFFER_SIZE);
|
|
|
|
|
} else {
|
|
|
|
|
dxfer_len = 0;
|
|
|
|
|
|
|
|
|
|
/* >>> remove this condition when #if LIBCDIO_VERSION_NUM < 83
|
|
|
|
|
is in effect above*/
|
|
|
|
|
#ifdef SCSI_MMC_HAS_DIR_NONE
|
|
|
|
|
e_direction = SCSI_MMC_DATA_NONE;
|
|
|
|
|
#else
|
|
|
|
|
e_direction = SCSI_MMC_DATA_READ;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* >>> longer timeout , sg-linux has 200 s */
|
|
|
|
|
|
|
|
|
|
/* >>> retry-loop */
|
|
|
|
|
/* retry-loop */
|
|
|
|
|
start_time = time(NULL);
|
|
|
|
|
timeout_ms = 200000;
|
|
|
|
|
for(i = 0; ; i++) {
|
|
|
|
|
|
|
|
|
|
i_status = mmc_run_cmd(p_cdio, 10000, &cdb, e_direction,
|
|
|
|
|
i_status = mmc_run_cmd(p_cdio, timeout_ms, &cdb, e_direction,
|
|
|
|
|
dxfer_len, c->page->data);
|
|
|
|
|
|
|
|
|
|
if (i_status == 0)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
/* >>> One would need to get info about the nature of failure
|
|
|
|
|
SCSI SK,ASC,ASCQ would be nice.
|
|
|
|
|
One would need to distinguish between drive error and
|
|
|
|
|
transport error.
|
|
|
|
|
*/;
|
|
|
|
|
sense_valid = mmc_last_cmd_sense(p_cdio, &sense_pt);
|
|
|
|
|
if (sense_valid >= 18)
|
|
|
|
|
memcpy(sense, sense_pt, 18);
|
|
|
|
|
if (sense_pt != NULL)
|
|
|
|
|
free(sense_pt);
|
|
|
|
|
|
|
|
|
|
/* This is for failure of the transport mechanism itself:
|
|
|
|
|
/* Regrettably mmc_run_cmd() does not clearly distinguish between transport
|
|
|
|
|
failure and SCSI error reply.
|
|
|
|
|
This reaction here would be for transport failure:
|
|
|
|
|
|
|
|
|
|
if (i_status != 0 && i_status != DRIVER_OP_ERROR) {
|
|
|
|
|
libdax_msgs_submit(libdax_messenger,
|
|
|
|
|
d->global_index, 0x0002010c,
|
|
|
|
|
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
|
|
|
|
@ -390,27 +459,56 @@ int sg_issue_command(struct burn_drive *d, struct command *c)
|
|
|
|
|
sg_close_drive(d);
|
|
|
|
|
d->released = 1;
|
|
|
|
|
d->busy = BURN_DRIVE_IDLE;
|
|
|
|
|
c->error = 1;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* >>> end retry-loop */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (c->opcode[0] != 0x00) {
|
|
|
|
|
sprintf(msg, "SCSI command %2.2Xh failed",
|
|
|
|
|
(unsigned int) c->opcode[0]);
|
|
|
|
|
libdax_msgs_submit(libdax_messenger,
|
|
|
|
|
d->global_index, 0x0002010f,
|
|
|
|
|
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
|
|
|
|
|
msg, errno, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 2 04 00 LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE */
|
|
|
|
|
c->sense[2] = 0x02;
|
|
|
|
|
c->sense[12] = 0x04;
|
|
|
|
|
c->sense[13] = 0x00;
|
|
|
|
|
|
|
|
|
|
c->error = 1;
|
|
|
|
|
return -1;
|
|
|
|
|
if (!sense_valid) {
|
|
|
|
|
memset(sense, 0, 18);
|
|
|
|
|
if (i_status != 0) { /* set dummy sense */
|
|
|
|
|
/*LOGICAL UNIT NOT READY,CAUSE NOT REPORTABLE*/
|
|
|
|
|
sense[2] = 0x02;
|
|
|
|
|
sense[12] = 0x04;
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
sense[2] &= 15;
|
|
|
|
|
|
|
|
|
|
if (i_status != 0 || (sense[2] || sense[12] || sense[13])) {
|
|
|
|
|
if (!c->retry) {
|
|
|
|
|
c->error = 1;
|
|
|
|
|
goto ex;
|
|
|
|
|
}
|
|
|
|
|
switch (scsi_error(d, sense, 18)) {
|
|
|
|
|
case RETRY:
|
|
|
|
|
break;
|
|
|
|
|
case FAIL:
|
|
|
|
|
c->error = 1;
|
|
|
|
|
goto ex;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
Calming down retries and breaking up endless cycle
|
|
|
|
|
*/
|
|
|
|
|
usleep_time = Libburn_sg_libcdio_retry_usleeP +
|
|
|
|
|
i * Libburn_sg_libcdio_retry_incR;
|
|
|
|
|
if (time(NULL) + usleep_time / 1000000 - start_time >
|
|
|
|
|
timeout_ms / 1000 + 1) {
|
|
|
|
|
c->error = 1;
|
|
|
|
|
goto ex;
|
|
|
|
|
}
|
|
|
|
|
usleep(usleep_time);
|
|
|
|
|
} else
|
|
|
|
|
break; /* retry-loop */
|
|
|
|
|
} /* end of retry-loop */
|
|
|
|
|
|
|
|
|
|
ex:;
|
|
|
|
|
if (c->error)
|
|
|
|
|
scsi_notify_error(d, c, sense, 18, 0);
|
|
|
|
|
|
|
|
|
|
if (burn_sg_log_scsi & 3)
|
|
|
|
|
/* >>> Need own duration time measurement. Then remove bit1 */
|
|
|
|
|
scsi_log_err(c, fp, sense, 0, (c->error != 0) | 2);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|