legacy/trunk/libburn/drive.c

536 lines
12 KiB
C

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
#include <malloc.h>
#include <unistd.h>
#include <signal.h>
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <pthread.h>
#include "libburn.h"
#include "drive.h"
#include "transport.h"
#include "message.h"
#include "debug.h"
#include "init.h"
#include "toc.h"
#include "util.h"
#include "sg.h"
#include "structure.h"
#include "back_hacks.h"
static struct burn_drive drive_array[255];
static int drivetop = -1;
int burn_drive_is_open(struct burn_drive *d);
/* 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 (burn_drive_is_open(d))
close(d->fd);
free((void *) d->idata);
free((void *) d->mdata);
free((void *) d->toc_entry);
free(d->devname);
d->global_index = -1;
}
void burn_drive_free_all(void)
{
int i;
for (i = 0; i < drivetop + 1; i++)
burn_drive_free(&(drive_array[i]));
drivetop = -1;
memset(drive_array, 0, sizeof(drive_array));
}
/*
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;
}
int burn_drive_grab(struct burn_drive *d, int le)
{
int errcode;
if (!d->released) {
burn_print(1, "can't grab - already grabbed\n");
return 0;
}
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);
d->status = BURN_DISC_BLANK;
if (d->mdata->cdr_write || d->mdata->cdrw_write ||
d->mdata->dvdr_write || d->mdata->dvdram_write) {
d->read_disc_info(d);
} else
d->read_toc(d);
d->busy = BURN_DRIVE_IDLE;
return 1;
}
struct burn_drive *burn_drive_register(struct burn_drive *d)
{
#ifdef Libburn_ticket_62_re_register_is_possiblE
int i;
#endif
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->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 */
/* 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 */
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 */
}
void burn_drive_release(struct burn_drive *d, int le)
{
if (d->released)
burn_print(1, "second release on drive!\n");
assert(!d->busy);
d->unlock(d);
if (le)
d->eject(d);
d->release(d);
d->status = BURN_DISC_UNREADY;
d->released = 1;
if (d->toc_entry)
free(d->toc_entry);
d->toc_entry = NULL;
d->toc_entries = 0;
if (d->disc != NULL) {
burn_disc_free(d->disc);
d->disc = NULL;
}
}
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;
assert(d->released);
}
if (!finished)
sleep(1);
}
}
void burn_disc_erase_sync(struct burn_drive *d, int fast)
{
burn_message_clear_queue();
burn_print(1, "erasing drive %s %s\n", d->idata->vendor,
d->idata->product);
/* ts A60825 : allow on parole to blank appendable CDs */
if ( ! (d->status == BURN_DISC_FULL ||
(d->status == BURN_DISC_APPENDABLE &&
! libburn_back_hack_42) ) )
return;
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->test_unit_ready(d) &&
(d->progress.sector = d->get_erase_progress(d)) > 0)
sleep(1);
d->progress.sector = 0x10000;
d->busy = BURN_DRIVE_IDLE;
}
enum burn_disc_status burn_disc_get_status(struct burn_drive *d)
{
assert(!d->released);
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)
{
if (p) {
memcpy(p, &(d->progress), sizeof(struct burn_progress));
/* TODO: add mutex */
}
return d->busy;
}
void burn_drive_cancel(struct burn_drive *d)
{
pthread_mutex_lock(&d->access_lock);
d->cancel = 1;
pthread_mutex_unlock(&d->access_lock);
}
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);
assert( /* (write_type >= BURN_WRITE_PACKET) && */
(write_type <= BURN_WRITE_RAW));
return d->block_types[write_type];
}
static void strip_spaces(char *str)
{
char *tmp;
tmp = str + strlen(str) - 1;
while (isspace(*tmp))
*(tmp--) = '\0';
tmp = str;
while (*tmp) {
if (isspace(*tmp) && isspace(*(tmp + 1))) {
char *tmp2;
for (tmp2 = tmp + 1; *tmp2; ++tmp2)
*(tmp2 - 1) = *tmp2;
*(tmp2 - 1) = '\0';
} else
++tmp;
}
}
static int drive_getcaps(struct burn_drive *d, struct burn_drive_info *out)
{
struct scsi_inquiry_data *id;
assert(d->idata);
assert(d->mdata);
if (!d->idata->valid || !d->mdata->valid)
return 0;
id = (struct scsi_inquiry_data *)d->idata;
memcpy(out->vendor, id->vendor, sizeof(id->vendor));
strip_spaces(out->vendor);
memcpy(out->product, id->product, sizeof(id->product));
strip_spaces(out->product);
memcpy(out->revision, id->revision, sizeof(id->revision));
strip_spaces(out->revision);
strncpy(out->location, d->devname, 16);
out->location[16] = '\0';
out->buffer_size = d->mdata->buffer_size;
out->read_dvdram = !!d->mdata->dvdram_read;
out->read_dvdr = !!d->mdata->dvdr_read;
out->read_dvdrom = !!d->mdata->dvdrom_read;
out->read_cdr = !!d->mdata->cdr_read;
out->read_cdrw = !!d->mdata->cdrw_read;
out->write_dvdram = !!d->mdata->dvdram_write;
out->write_dvdr = !!d->mdata->dvdr_write;
out->write_cdr = !!d->mdata->cdr_write;
out->write_cdrw = !!d->mdata->cdrw_write;
out->write_simulate = !!d->mdata->simulate;
out->c2_errors = !!d->mdata->c2_pointers;
out->drive = d;
/* update available block types for burners */
if (out->write_dvdram || out->write_dvdr ||
out->write_cdrw || out->write_cdr)
d->probe_write_modes(d);
out->tao_block_types = d->block_types[BURN_WRITE_TAO];
out->sao_block_types = d->block_types[BURN_WRITE_SAO];
out->raw_block_types = d->block_types[BURN_WRITE_RAW];
out->packet_block_types = d->block_types[BURN_WRITE_PACKET];
return 1;
}
int burn_drive_scan_sync(struct burn_drive_info *drives[],
unsigned int *n_drives)
{
/* state vars for the scan process */
/* ts A60904 : did set some default values to feel comfortable */
static int scanning = 0, scanned = 0, found = 0;
static unsigned num_scanned = 0, count = 0;
unsigned int i;
#ifdef Libburn_ticket_62_enable_redundant_asserT
struct burn_drive *d;
#endif
assert(burn_running);
if (!scanning) {
scanning = 1;
/* make sure the drives aren't in use */
burn_wait_all(); /* make sure the queue cleans up
before checking for the released
state */
/* ts A60904 : ticket 62, contribution by elmom */
/* this is redundant with what is done in burn_wait_all() */
#ifdef Libburn_ticket_62_enable_redundant_asserT
d = drive_array;
count = burn_drive_count();
for (i = 0; i < count; ++i, ++d)
assert(d->released == 1);
#endif /* Libburn_ticket_62_enable_redundant_asserT */
/* refresh the lib's drives */
sg_enumerate();
ata_enumerate();
count = burn_drive_count();
if (count)
*drives =
malloc(sizeof(struct burn_drive_info) * count);
else
*drives = NULL;
*n_drives = scanned = found = num_scanned = 0;
}
for (i = 0; i < count; ++i) {
if (scanned & (1 << i))
continue; /* already scanned the device */
while (!drive_getcaps(&drive_array[i],
&(*drives)[num_scanned])) {
sleep(1);
}
scanned |= 1 << i;
found |= 1 << i;
num_scanned++;
(*n_drives)++;
}
if (num_scanned == count) {
/* done scanning */
scanning = 0;
return 1;
}
return 0;
}
void burn_drive_info_free(struct burn_drive_info drive_infos[])
{
/* ts A60904 : ticket 62, contribution by elmom */
/* clarifying the meaning and the identity of the victim */
/* ts A60904 : This looks a bit weird.
burn_drive_info is not the manager of burn_drive but only its
spokesperson. To my knowlege drive_infos from burn_drive_scan()
are not memorized globally. */
if(drive_infos != NULL) /* and this NULL test is deadly necessary */
free((void *) drive_infos);
burn_drive_free_all();
}
/* Experimental API call */
int burn_drive_info_forget(struct burn_drive_info *info)
{
burn_drive_free(info->drive);
return 1;
}
struct burn_disc *burn_drive_get_disc(struct burn_drive *d)
{
d->disc->refcnt++;
return d->disc;
}
void burn_drive_set_speed(struct burn_drive *d, int r, int w)
{
d->set_speed(d, r, w);
}
int burn_msf_to_sectors(int m, int s, int f)
{
return (m * 60 + s) * 75 + f;
}
void burn_sectors_to_msf(int sectors, int *m, int *s, int *f)
{
*m = sectors / (60 * 75);
*s = (sectors - *m * 60 * 75) / 75;
*f = sectors - *m * 60 * 75 - *s * 75;
}
int burn_drive_get_read_speed(struct burn_drive *d)
{
return d->mdata->max_read_speed;
}
int burn_drive_get_write_speed(struct burn_drive *d)
{
return d->mdata->max_write_speed;
}
/* ts A51221 */
static char *enumeration_whitelist[BURN_DRIVE_WHITELIST_LEN];
static int enumeration_whitelist_top = -1;
/** Add a device to the list of permissible drives. As soon as some entry is in
the whitelist all non-listed drives are banned from enumeration.
@return 1 success, <=0 failure
*/
int burn_drive_add_whitelist(char *device_address)
{
char *new_item;
if(enumeration_whitelist_top+1 >= BURN_DRIVE_WHITELIST_LEN)
return 0;
enumeration_whitelist_top++;
new_item = malloc(strlen(device_address) + 1);
if (new_item == NULL)
return -1;
strcpy(new_item, device_address);
enumeration_whitelist[enumeration_whitelist_top] = new_item;
return 1;
}
/** Remove all drives from whitelist. This enables all possible drives. */
void burn_drive_clear_whitelist(void)
{
int i;
for (i = 0; i <= enumeration_whitelist_top; i++)
free(enumeration_whitelist[i]);
enumeration_whitelist_top = -1;
}
int burn_drive_is_banned(char *device_address)
{
int i;
if(enumeration_whitelist_top<0)
return 0;
for (i = 0; i <= enumeration_whitelist_top; i++)
if (strcmp(enumeration_whitelist[i], device_address) == 0)
return 0;
return 1;
}
/* ts A60822 */
int burn_drive_is_open(struct burn_drive *d)
{
/* a bit more detailed case distinction than needed */
if (d->fd == -1337)
return 0;
if (d->fd < 0)
return 0;
return 1;
}
/* ts A60823 */
/** Aquire a drive with known persistent address.
*/
int burn_drive_scan_and_grab(struct burn_drive_info *drive_infos[], char* adr,
int load)
{
unsigned int n_drives;
int ret;
burn_drive_clear_whitelist();
burn_drive_add_whitelist(adr);
while (!burn_drive_scan(drive_infos, &n_drives));
if (n_drives <= 0)
return 0;
if (load) {
/* RIP-14.5 + LITE-ON 48125S produce a false status
if tray was unloaded */
/* Therefore the first grab is just for loading */
ret= burn_drive_grab(drive_infos[0]->drive, 1);
if (ret != 1)
return -1;
burn_drive_release(drive_infos[0]->drive,0);
}
ret = burn_drive_grab(drive_infos[0]->drive, load);
if (ret != 1)
return -1;
return 1;
}
/* ts A60823 */
/** Inquire the persistent address of the given drive. */
int burn_drive_get_adr(struct burn_drive_info *drive, char adr[])
{
assert(strlen(drive->location) < BURN_DRIVE_ADR_LEN);
strcpy(adr,drive->location);
return 1;
}