You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libburn/libburn/drive.c

2576 lines
64 KiB

17 years ago
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#include <sys/types.h>
#include <sys/stat.h>
/* #include <m alloc.h> ts A61013 : not in Linux man 3 malloc */
#include <stdlib.h>
17 years ago
#include <unistd.h>
#include <signal.h>
/* ts A61007 */
/* #include <a ssert.h> */
17 years ago
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include <errno.h>
17 years ago
#include "libburn.h"
#include "init.h"
17 years ago
#include "drive.h"
#include "transport.h"
#include "debug.h"
#include "init.h"
#include "toc.h"
#include "util.h"
#include "sg.h"
#include "structure.h"
/* ts A70107 : to get BE_CANCELLED */
#include "error.h"
/* ts A70219 : for burn_disc_get_write_mode_demands() */
#include "options.h"
/* A70225 : to learn about eventual Libburn_dvd_r_dl_multi_no_close_sessioN */
#include "write.h"
/* A70903 : for burn_scsi_setup_drive() */
#include "spc.h"
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
17 years ago
static struct burn_drive drive_array[255];
static int drivetop = -1;
/* ts A80410 : in init.c */
extern int burn_support_untested_profiles;
/* ts A61021 : the unspecific part of sg.c:enumerate_common()
*/
int burn_setup_drive(struct burn_drive *d, char *fname)
{
d->devname = burn_strdup(fname);
memset(&d->params, 0, sizeof(struct params));
d->idata = NULL;
d->mdata = NULL;
d->toc_entry = NULL;
d->released = 1;
d->stdio_fd = -1;
d->status = BURN_DISC_UNREADY;
d->do_stream_recording = 0;
d->stream_recording_start= 0;
return 1;
}
/* ts A70903 */
void burn_drive_free_subs(struct burn_drive *d)
{
if (d->idata != NULL)
free((void *) d->idata);
d->idata = NULL;
if (d->mdata != NULL) {
burn_mdata_free_subs(d->mdata);
free((void *) d->mdata);
}
d->mdata = NULL;
if(d->toc_entry != NULL)
free((void *) d->toc_entry);
d->toc_entry = NULL;
if (d->devname != NULL)
free(d->devname);
d->devname = NULL;
if (d->stdio_fd >= 0)
close (d->stdio_fd);
d->stdio_fd = -1;
}
/* ts A60904 : ticket 62, contribution by elmom */
/* splitting former burn_drive_free() (which freed all, into two calls) */
void burn_drive_free(struct burn_drive *d)
{
if (d->global_index == -1)
return;
/* ts A60822 : close open fds before forgetting them */
if (d->drive_role == 1)
if (burn_drive_is_open(d)) {
d->unlock(d);
d->release(d);
}
burn_drive_free_subs(d);
d->global_index = -1;
}
void burn_drive_free_all(void)
17 years ago
{
int i;
for (i = 0; i < drivetop + 1; i++)
burn_drive_free(&(drive_array[i]));
17 years ago
drivetop = -1;
memset(drive_array, 0, sizeof(drive_array));
}
/* ts A60822 */
int burn_drive_is_open(struct burn_drive *d)
{
if (d->drive_role != 1)
return (d->stdio_fd >= 0);
/* ts A61021 : moved decision to sg.c */
return d->drive_is_open(d);
}
/* ts A60906 */
int burn_drive_force_idle(struct burn_drive *d)
{
d->busy = BURN_DRIVE_IDLE;
return 1;
}
/* ts A60906 */
int burn_drive_is_released(struct burn_drive *d)
{
return !!d->released;
}
/* ts A60906 */
/** Inquires drive status in respect to degree of app usage.
@param return -2 = drive is forgotten
-1 = drive is closed (i.e. released explicitely)
0 = drive is open, not grabbed (after scan, before 1st grab)
1 = drive is grabbed but BURN_DRIVE_IDLE
2 = drive is grabbed, synchronous read/write interrupted
10 = drive is grabbing (BURN_DRIVE_GRABBING)
100 = drive is busy in cancelable state
1000 = drive is in non-cancelable state
Expect a monotonous sequence of usage severity to emerge in future.
*/
int burn_drive_is_occupied(struct burn_drive *d)
{
if(d->global_index < 0)
return -2;
if(!burn_drive_is_open(d))
return -1;
if(d->busy == BURN_DRIVE_GRABBING)
return 10;
if(d->released)
return 0;
if(d->busy == BURN_DRIVE_IDLE)
return 1;
if(d->busy == BURN_DRIVE_READING_SYNC ||
d->busy == BURN_DRIVE_WRITING_SYNC)
return 2;
if(d->busy == BURN_DRIVE_WRITING) {
/* ts A70928 */
/* >>> how do i learn whether the writer thread is still
alive ? */;
/* >>> what to do if writer is dead ?
At least sync disc ?*/;
return 50;
}
if(d->busy == BURN_DRIVE_READING) {
return 50;
}
return 1000;
}
17 years ago
/*
void drive_read_lead_in(int dnum)
{
mmc_read_lead_in(&drive_array[dnum], get_4k());
}
*/
unsigned int burn_drive_count(void)
{
return drivetop + 1;
}
/* ts A80801 */
int burn_drive_is_listed(char *path, struct burn_drive **found, int flag)
{
int i, ret;
char drive_adr[BURN_DRIVE_ADR_LEN], off_adr[BURN_DRIVE_ADR_LEN];
ret = burn_drive_convert_fs_adr(path, off_adr);
if (ret <= 0)
strcpy(off_adr, path);
for (i = 0; i <= drivetop; i++) {
if (drive_array[i].global_index < 0)
continue;
ret = burn_drive_d_get_adr(&(drive_array[i]), drive_adr);
if (ret <= 0)
continue;
if(strcmp(off_adr, drive_adr) == 0) {
if (found != NULL)
*found= &(drive_array[i]);
return 1;
}
}
return 0;
}
/* ts A61125 : media status aspects of burn_drive_grab() */
int burn_drive_inquire_media(struct burn_drive *d)
{
17 years ago
/* ts A61225 : after loading the tray, mode page 2Ah can change */
d->getcaps(d);
/* ts A61020 : d->status was set to BURN_DISC_BLANK as pure guess */
/* ts A71128 : run read_disc_info() for any recognizeable profile */
if (d->current_profile > 0 ||
d->mdata->cdr_write || d->mdata->cdrw_write ||
17 years ago
d->mdata->dvdr_write || d->mdata->dvdram_write) {
#define Libburn_knows_correct_state_after_loaD 1
#ifdef Libburn_knows_correct_state_after_loaD
17 years ago
d->read_disc_info(d);
#else
/* ts A61227 : This repeated read_disc_info seems
to be obsoleted by above d->getcaps(d).
*/
/* ts A60908 */
/* Trying to stabilize the disc status after eventual load
without closing and re-opening the drive */
/* This seems to work for burn_disc_erasable() .
Speed values on RIP-14 and LITE-ON 48125S are stable
and false, nevertheless. */
int was_equal = 0, must_equal = 3, max_loop = 20;
int loop_count, old_speed = -1234567890, new_speed= -987654321;
int old_erasable = -1234567890, new_erasable = -987654321;
fprintf(stderr,"LIBBURN_DEBUG: read_disc_info()\n");
for (loop_count = 0; loop_count < max_loop; loop_count++){
old_speed = new_speed;
old_erasable = new_erasable;
d->read_disc_info(d);
if(d->status == BURN_DISC_UNSUITABLE)
break;
new_speed = burn_drive_get_write_speed(d);
new_erasable = burn_disc_erasable(d);
if (new_speed == old_speed &&
new_erasable == old_erasable) {
was_equal++;
if (was_equal >= must_equal)
break;
} else
was_equal = 0;
/*
if (loop_count >= 1 && was_equal == 0)
*/
fprintf(stderr,"LIBBURN_DEBUG: %d : speed %d:%d erasable %d:%d\n",
loop_count,old_speed,new_speed,old_erasable,new_erasable);
usleep(100000);
}
#endif /* ! Libburn_knows_correct_state_after_loaD */
} else {
if (d->current_profile == -1 || d->current_is_cd_profile)
d->read_toc(d);
/* ts A70314 */
d->status = BURN_DISC_UNSUITABLE;
}
17 years ago
return 1;
}
int burn_drive_grab(struct burn_drive *d, int le)
{
int errcode;
/* ts A61125 - A61202 */
int ret, sose;
if (!d->released) {
burn_print(1, "can't grab - already grabbed\n");
return 0;
}
if(d->drive_role != 1) {
d->released = 0;
if(d->drive_role == 2 || d->drive_role == 3) {
d->status = BURN_DISC_BLANK;
d->current_profile = 0xffff;
} else {
d->status = BURN_DISC_EMPTY;
d->current_profile = 0;
}
d->busy = BURN_DRIVE_IDLE;
return 1;
}
d->status = BURN_DISC_UNREADY;
errcode = d->grab(d);
if (errcode == 0) {
burn_print(1, "low level drive grab failed\n");
return 0;
}
d->busy = BURN_DRIVE_GRABBING;
if (le)
d->load(d);
d->lock(d);
/* ts A61118 */
d->start_unit(d);
/* ts A61202 : gave bit1 of le a meaning */
sose = d->silent_on_scsi_error;
if (!le)
d->silent_on_scsi_error = 1;
/* ts A61125 : outsourced media state inquiry aspects */
ret = burn_drive_inquire_media(d);
d->silent_on_scsi_error = sose;
d->busy = BURN_DRIVE_IDLE;
return ret;
}
/* ts A71015 */
#define Libburn_ticket_62_re_register_is_possiblE 1
17 years ago
struct burn_drive *burn_drive_register(struct burn_drive *d)
{
#ifdef Libburn_ticket_62_re_register_is_possiblE
int i;
#endif
17 years ago
d->block_types[0] = 0;
d->block_types[1] = 0;
d->block_types[2] = 0;
d->block_types[3] = 0;
d->toc_temp = 0;
d->nwa = 0;
d->alba = 0;
d->rlba = 0;
d->cancel = 0;
d->busy = BURN_DRIVE_IDLE;
d->thread_pid = 0;
d->thread_pid_valid = 0;
17 years ago
d->toc_entries = 0;
d->toc_entry = NULL;
d->disc = NULL;
d->erasable = 0;
#ifdef Libburn_ticket_62_re_register_is_possiblE
/* ts A60904 : ticket 62, contribution by elmom */
/* Not yet accepted because no use case seen yet */
/* ts A71015 : xorriso dialog imposes a use case now */
/* This is supposed to find an already freed drive struct among
all the the ones that have been used before */
for (i = 0; i < drivetop + 1; i++)
if (drive_array[i].global_index == -1)
break;
d->global_index = i;
memcpy(&drive_array[i], d, sizeof(struct burn_drive));
pthread_mutex_init(&drive_array[i].access_lock, NULL);
if (drivetop < i)
drivetop = i;
return &(drive_array[i]);
#else /* Libburn_ticket_62_re_register_is_possiblE */
/* old A60904 : */
/* Still active by default */
d->global_index = drivetop + 1;
17 years ago
memcpy(&drive_array[drivetop + 1], d, sizeof(struct burn_drive));
pthread_mutex_init(&drive_array[drivetop + 1].access_lock, NULL);
return &drive_array[++drivetop];
#endif /* ! Libburn_ticket_62_re_register_is_possiblE */
17 years ago
}
/* unregister most recently registered drive */
int burn_drive_unregister(struct burn_drive *d)
{
if(d->global_index != drivetop)
return 0;
burn_drive_free(d);
drivetop--;
return 1;
}
/* ts A61021 : after-setup activities from sg.c:enumerate_common()
*/
struct burn_drive *burn_drive_finish_enum(struct burn_drive *d)
{
struct burn_drive *t;
char msg[BURN_DRIVE_ADR_LEN + 160];
int ret;
/* ts A60821
<<< debug: for tracing calls which might use open drive fds */
int mmc_function_spy(struct burn_drive *d, char * text);
d->drive_role = 1; /* MMC drive */
t = burn_drive_register(d);
/* ts A60821 */
mmc_function_spy(NULL, "enumerate_common : -------- doing grab");
/* try to get the drive info */
ret = t->grab(t);
if (ret) {
burn_print(2, "getting drive info\n");
t->getcaps(t);
t->unlock(t);
t->released = 1;
} else {
/* ts A90602 */
d->mdata->valid = -1;
sprintf(msg, "Unable to grab scanned drive %s", d->devname);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002016f, LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_LOW, msg, 0, 0);
burn_drive_unregister(t);
t = NULL;
}
/* ts A60821 */
mmc_function_spy(NULL, "enumerate_common : ----- would release ");
return t;
}
/* ts A61125 : model aspects of burn_drive_release */
int burn_drive_mark_unready(struct burn_drive *d)
{
/* ts A61020 : mark media info as invalid */
d->start_lba= -2000000000;
d->end_lba= -2000000000;
/* ts A61202 */
d->current_profile = -1;
d->current_has_feat21h = 0;
d->current_feat2fh_byte4 = -1;
d->status = BURN_DISC_UNREADY;
if (d->toc_entry != NULL)
free(d->toc_entry);
d->toc_entry = NULL;
d->toc_entries = 0;
if (d->disc != NULL) {
burn_disc_free(d->disc);
d->disc = NULL;
}
if (d->stdio_fd >= 0)
close (d->stdio_fd);
d->stdio_fd = -1;
return 1;
}
/* ts A70918 : outsourced from burn_drive_release() and enhanced */
/** @param flag bit0-2 = mode : 0=unlock , 1=unlock+eject , 2=leave locked
*/
int burn_drive_release_fl(struct burn_drive *d, int flag)
17 years ago
{
if (d->released) {
/* ts A61007 */
/* burn_print(1, "second release on drive!\n"); */
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x00020105,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Drive is already released", 0, 0);
return 0;
}
/* ts A61007 */
/* ts A60906: one should not assume BURN_DRIVE_IDLE == 0 */
/* a ssert(d->busy == BURN_DRIVE_IDLE); */
if (d->busy != BURN_DRIVE_IDLE) {
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x00020106,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Drive is busy on attempt to close", 0, 0);
return 0;
}
if (d->drive_role == 1) {
if (d->needs_sync_cache)
d->sync_cache(d);
if ((flag & 7) != 2)
d->unlock(d);
if ((flag & 7) == 1)
d->eject(d);
d->release(d);
}
d->needs_sync_cache = 0; /* just to be sure */
17 years ago
d->released = 1;
/* ts A61125 : outsourced model aspects */
burn_drive_mark_unready(d);
return 1;
17 years ago
}
/* API */
void burn_drive_release(struct burn_drive *d, int le)
{
burn_drive_release_fl(d, !!le);
}
/* ts A70918 */
/* API */
int burn_drive_leave_locked(struct burn_drive *d, int flag)
{
return burn_drive_release_fl(d, 2);
}
/* ts A61007 : former void burn_wait_all() */
/* @param flag bit0= demand freed drives (else released drives) */
int burn_drives_are_clear(int flag)
{
int i;
for (i = burn_drive_count() - 1; i >= 0; --i) {
/* ts A60904 : ticket 62, contribution by elmom */
if (drive_array[i].global_index == -1)
continue;
if (drive_array[i].released && !(flag & 1))
continue;
return 0;
}
return 1;
}
#if 0
17 years ago
void burn_wait_all(void)
{
unsigned int i;
int finished = 0;
struct burn_drive *d;
while (!finished) {
finished = 1;
d = drive_array;
for (i = burn_drive_count(); i > 0; --i, ++d) {
/* ts A60904 : ticket 62, contribution by elmom */
if (d->global_index==-1)
continue;
a ssert(d->released);
17 years ago
}
if (!finished)
sleep(1);
}
}
#endif
17 years ago
void burn_disc_erase_sync(struct burn_drive *d, int fast)
{
/* ts A60924 : libburn/message.c gets obsoleted
17 years ago
burn_message_clear_queue();
*/
17 years ago
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
d->idata->product);
d->cancel = 0;
d->busy = BURN_DRIVE_ERASING;
d->erase(d, fast);
/* reset the progress */
d->progress.session = 0;
d->progress.sessions = 1;
d->progress.track = 0;
d->progress.tracks = 1;
d->progress.index = 0;
d->progress.indices = 1;
d->progress.start_sector = 0;
d->progress.sectors = 0x10000;
d->progress.sector = 0;
/* read the initial 0 stage */
while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0)
sleep(1);
while ((d->progress.sector = d->get_erase_progress(d)) > 0 ||
!d->test_unit_ready(d))
17 years ago
sleep(1);
d->progress.sector = 0x10000;
/* ts A61125 : update media state records */
burn_drive_mark_unready(d);
if (d->drive_role == 1)
burn_drive_inquire_media(d);
d->busy = BURN_DRIVE_IDLE;
17 years ago
}
/*
@param flag: bit0 = fill formatted size with zeros
bit1, bit2 , bit4, bit5, bit7 - bit15 are for d->format_unit()
*/
void burn_disc_format_sync(struct burn_drive *d, off_t size, int flag)
{
int ret, buf_secs, err, i, stages = 1, pbase, pfill, pseudo_sector;
off_t num_bufs;
char msg[80];
struct buffer buf, *buf_mem = d->buffer;
/* reset the progress */
d->progress.session = 0;
d->progress.sessions = 1;
d->progress.track = 0;
d->progress.tracks = 1;
d->progress.index = 0;
d->progress.indices = 1;
d->progress.start_sector = 0;
d->progress.sectors = 0x10000;
d->progress.sector = 0;
stages = 1 + ((flag & 1) && size > 1024 * 1024);
d->cancel = 0;
d->busy = BURN_DRIVE_FORMATTING;
ret = d->format_unit(d, size, flag & 0xfff6); /* forward bits */
if (ret <= 0)
d->cancel = 1;
while (!d->test_unit_ready(d) && d->get_erase_progress(d) == 0)
sleep(1);
while ((pseudo_sector = d->get_erase_progress(d)) > 0 ||
!d->test_unit_ready(d)) {
d->progress.sector = pseudo_sector / stages;
sleep(1);
}
d->sync_cache(d);
if (size <= 0)
goto ex;
/* update media state records */
burn_drive_mark_unready(d);
burn_drive_inquire_media(d);
if (flag & 1) {
/* write size in zeros */;
pbase = 0x8000 + 0x7fff * (stages == 1);
pfill = 0xffff - pbase;
buf_secs = 16; /* Must not be more than 16 */
num_bufs = size / buf_secs / 2048;
if (num_bufs > 0x7fffffff) {
d->cancel = 1;
goto ex;
}
/* <<< */
sprintf(msg,
"Writing %.f sectors of zeros to formatted media",
(double) num_bufs * (double) buf_secs);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
msg, 0, 0);
d->buffer = &buf;
memset(d->buffer, 0, sizeof(struct buffer));
d->buffer->bytes = buf_secs * 2048;
d->buffer->sectors = buf_secs;
d->busy = BURN_DRIVE_WRITING;
for (i = 0; i < num_bufs; i++) {
d->nwa = i * buf_secs;
err = d->write(d, d->nwa, d->buffer);
if (err == BE_CANCELLED || d->cancel) {
d->cancel = 1;
break;
}
d->progress.sector = pbase
+ pfill * ((double) i / (double) num_bufs);
}
d->sync_cache(d);
if (d->current_profile == 0x13 || d->current_profile == 0x1a) {
/* DVD-RW or DVD+RW */
d->busy = BURN_DRIVE_CLOSING_SESSION;
/* CLOSE SESSION, 010b */
d->close_track_session(d, 1, 0);
d->busy = BURN_DRIVE_WRITING;
}
}
ex:;
d->progress.sector = 0x10000;
d->busy = BURN_DRIVE_IDLE;
d->buffer = buf_mem;
}
/* ts A70112 API */
int burn_disc_get_formats(struct burn_drive *d, int *status, off_t *size,
unsigned *bl_sas, int *num_formats)
{
int ret;
*status = 0;
*size = 0;
*bl_sas = 0;
*num_formats = 0;
if (d->drive_role != 1)
return 0;
ret = d->read_format_capacities(d, 0x00);
if (ret <= 0)
return 0;
*status = d->format_descr_type;
*size = d->format_curr_max_size;
*bl_sas = d->format_curr_blsas;
*num_formats = d->num_format_descr;
return 1;
}
/* ts A70112 API */
int burn_disc_get_format_descr(struct burn_drive *d, int index,
int *type, off_t *size, unsigned *tdp)
{
*type = 0;
*size = 0;
*tdp = 0;
if (index < 0 || index >= d->num_format_descr)
return 0;
*type = d->format_descriptors[index].type;
*size = d->format_descriptors[index].size;
*tdp = d->format_descriptors[index].tdp;
return 1;
}
17 years ago
enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
{
/* ts A61007 */
/* a ssert(!d->released); */
if (d->released) {
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x00020108,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Drive is not grabbed on disc status inquiry",
0, 0);
return BURN_DISC_UNGRABBED;
}
17 years ago
return d->status;
}
int burn_disc_erasable(struct burn_drive *d)
{
return d->erasable;
}
enum burn_drive_status burn_drive_get_status(struct burn_drive *d,
struct burn_progress *p)
{
/* ts A70928 : inform control thread of signal in sub-threads */
if (burn_global_abort_level > 0)
burn_global_abort_level++;
if (burn_global_abort_level > 5) {
if (burn_global_signal_handler == NULL)
kill(getpid(), burn_global_abort_signum);
else
(*burn_global_signal_handler)
(burn_global_signal_handle,
burn_global_abort_signum, 0);
burn_global_abort_level = -1;
}
if (p != NULL) {
17 years ago
memcpy(p, &(d->progress), sizeof(struct burn_progress));
/* TODO: add mutex */
}
return d->busy;
}
int burn_drive_set_stream_recording(struct burn_drive *d, int recmode,
int start, int flag)
{
if (recmode == 1)
d->do_stream_recording = 1;
else if (recmode == -1)
d->do_stream_recording = 0;
d->stream_recording_start = start;
return(1);
}
17 years ago
void burn_drive_cancel(struct burn_drive *d)
{
pthread_mutex_lock(&d->access_lock);
d->cancel = 1;
pthread_mutex_unlock(&d->access_lock);
}
/* ts A61007 : defunct because unused */
#if 0
17 years ago
int burn_drive_get_block_types(struct burn_drive *d,
enum burn_write_types write_type)
{
burn_print(12, "write type: %d\n", write_type);
a ssert( /* (write_type >= BURN_WRITE_PACKET) && */
17 years ago
(write_type <= BURN_WRITE_RAW));
return d->block_types[write_type];
}
#endif
17 years ago
static void strip_spaces(char *str)
{
char *tmp;
tmp = str + strlen(str) - 1;