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.
 
 
 
 
 
 

5936 lines
167 KiB

/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
Copyright (c) 2006 - 2016 Thomas Schmitt <scdbackup@gmx.net>
Provided under GPL version 2 or later.
*/
#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif
/* ts A61009 */
/* #include <a ssert.h> */
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include <ctype.h>
/* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */
#ifndef O_BINARY
#define O_BINARY 0
#endif
#include "error.h"
#include "sector.h"
#include "libburn.h"
#include "transport.h"
#include "mmc.h"
#include "spc.h"
#include "drive.h"
#include "debug.h"
#include "toc.h"
#include "structure.h"
#include "options.h"
#include "util.h"
#include "init.h"
/* ts A70223 : in init.c */
extern int burn_support_untested_profiles;
static int mmc_get_configuration_al(struct burn_drive *d, int *alloc_len);
#ifdef Libburn_log_in_and_out_streaM
/* <<< ts A61031 */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif /* Libburn_log_in_and_out_streaM */
/* ts A61005 */
#include "libdax_msgs.h"
extern struct libdax_msgs *libdax_messenger;
/* ts A61219 : Based on knowledge from dvd+rw-tools-7.0 and mmc5r03c.pdf */
#define Libburn_support_dvd_plus_rW 1
/* ts A61229 */
#define Libburn_support_dvd_minusrw_overW 1
/* ts A70112 */
/* ts A80410 : applies to BD-RE too */
#define Libburn_support_dvd_raM 1
/* ts A70129 */
#define Libburn_support_dvd_r_seQ 1
/* ts A70306 */
#define Libburn_support_dvd_plus_R 1
/* ts A70509 : handling 0x41 as read-only type */
#define Libburn_support_bd_r_readonlY 1
/* ts A81208 */
#define Libburn_support_bd_plus_r_srM 1
/* ts A80410 : <<< Dangerous experiment: Pretend that DVD-RAM is BD-RE
# define Libburn_dvd_ram_as_bd_rE yes
*/
/* ts A80509 : <<< Experiment: pretend that DVD-ROM and CD-ROM are other media
like BD-ROM (0x40), BD-R seq (0x41), BD-R random (0x42)
# define Libburn_rom_as_profilE 0x40
*/
/* ts A80425 : Prevents command FORMAT UNIT for DVD-RAM or BD-RE.
Useful only to test the selection of format descriptors without
actually formatting the media.
# define Libburn_do_not_format_dvd_ram_or_bd_rE 1
*/
/* ts A90603 : Simulate the command restrictions of an old MMC-1 drive
# define Libisofs_simulate_old_mmc1_drivE 1
*/
/* DVD/BD progress report:
ts A61219 : It seems to work with a used (i.e. thoroughly formatted) DVD+RW.
Error messages of class DEBUG appear because of inability to
read TOC or track info. Nevertheless, the written images verify.
ts A61220 : Burned to a virgin DVD+RW by help of new mmc_format_unit()
(did not test whether it would work without). Burned to a
not completely formatted DVD+RW. (Had worked before without
mmc_format_unit() but i did not exceed the formatted range
as reported by dvd+rw-mediainfo.)
ts A61221 : Speed setting now works for both of my drives. The according
functions in dvd+rw-tools are a bit intimidating to the reader.
I hope it is possible to leave much of this to the drive.
And if it fails ... well, it's only speed setting. :))
ts A61229 : Burned to several DVD-RW formatted to mode Restricted Overwrite
by dvd+rw-format. Needs Libburn_support_dvd_minusrw_overW.
ts A61230 : Other than growisofs, libburn does not send a mode page 5 for
such DVD-RW (which the MMC-5 standard does deprecate) and it
really seems to work without such a page.
ts A70101 : Formatted DVD-RW media. Success is varying with media, but
dvd+rw-format does not do better with the same media.
ts A70112 : Support for writing to DVD-RAM.
ts A70130 : Burned a first non-multi sequential DVD-RW. Feature 0021h
Incremental Recording vanishes after that and media thus gets
not recognized as suitable any more.
After a run with -multi another disc still offers 0021h .
dvd+rw-mediainfo shows two tracks. The second, an afio archive
is readable by afio. Third and forth veryfy too. Suddenly
dvd+rw-mediainfo sees lba 0 with track 2. But #2 still verifies
if one knows its address.
ts A70203 : DVD-RW need to get blanked fully. Then feature 0021h persists.
Meanwhile Incremental streaming is supported like CD TAO:
with unpredicted size, multi-track, multi-session.
ts A70205 : Beginning to implement DVD-R[W] DAO : single track and session,
size prediction mandatory.
ts A70208 : Finally made tests with DVD-R. Worked exactly as new DVD-RW.
ts A70306 : Implemented DVD+R (always -multi for now)
ts A70330 : Allowed finalizing of DVD+R.
ts A80228 : Made DVD+R/DL support official after nightmorph reported success
in http://libburnia-project.org/ticket/13
ts A80416 : drive->do_stream_recording brings DVD-RAM to full nominal
writing speed at cost of no defect management.
ts A80416 : Giulio Orsero reports success with BD-RE writing. With
drive->do_stream_recording it does full nominal speed.
ts A80506 : Giulio Orsero reports success with BD-RE formatting.
BD-RE is now an officially supported profile.
ts A81209 : The first two sessions have been written to BD-R SRM
(auto formatted without Defect Management).
ts A90107 : BD-R is now supported media type
*/
/* ts A70519 : With MMC commands of data direction FROM_DRIVE:
Made struct command.dxfer_len equal to Allocation Length
of MMC commands. Made sure that not more bytes are allowed
for transfer than there are available.
*/
/* ts A70711 Trying to keep writing from clogging the SCSI driver due to
full buffer at burner drive: 0=waiting disabled, 1=enabled
These are only defaults which can be overwritten by
burn_drive_set_buffer_waiting()
*/
#define Libburn_wait_for_buffer_freE 0
#define Libburn_wait_for_buffer_min_useC 10000
#define Libburn_wait_for_buffer_max_useC 100000
#define Libburn_wait_for_buffer_tio_seC 120
#define Libburn_wait_for_buffer_min_perC 65
#define Libburn_wait_for_buffer_max_perC 95
/* ts B31107 The minimum values to be applied if maximum read speed is
requested. Some drives tell only the currently set speed and
thus cannot be made faster by using the highest told value.
(The fractions get added or subtracted to yield an integer
number on the safe side of the intended limit.)
*/
#define Libburn_cd_max_read_speeD (52 * 150)
#define Libburn_dvd_max_read_speeD (24 * 1385)
#define Libburn_bd_max_read_speeD (20 * 4495.625 + 0.5)
/* ts B31114 The maximum values for minimum speed
*/
#define Libburn_cd_min_read_speeD ( 1 * 150)
#define Libburn_dvd_min_read_speeD ( 1 * 1385)
#define Libburn_bd_min_read_speeD ( 1 * 4495.625 - 0.625)
static unsigned char MMC_GET_MSINFO[] =
{ 0x43, 0, 1, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_TOC_FMT0[] = { 0x43, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_ATIP[] = { 0x43, 2, 4, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_GET_LEADTEXT[] = { 0x43, 2, 5, 0, 0, 0, 0, 4, 0, 0 };
static unsigned char MMC_GET_DISC_INFO[] =
{ 0x51, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_READ_CD[] = { 0xBE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_BLANK[] = { 0xA1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_SEND_OPC[] = { 0x54, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_SET_SPEED[] =
{ 0xBB, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_WRITE_12[] =
{ 0xAA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_WRITE_10[] = { 0x2A, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* ts A61201 : inserted 0, before 16, */
static unsigned char MMC_GET_CONFIGURATION[] =
{ 0x46, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_SYNC_CACHE[] = { 0x35, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_GET_EVENT[] = { 0x4A, 1, 0, 0, 0x7e, 0, 0, 0, 8, 0 };
static unsigned char MMC_CLOSE[] = { 0x5B, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char MMC_TRACK_INFO[] = { 0x52, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
static unsigned char MMC_SEND_CUE_SHEET[] =
{ 0x5D, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
/* ts A61023 : get size and free space of drive buffer */
static unsigned char MMC_READ_BUFFER_CAPACITY[] =
{ 0x5C, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
/* ts A61219 : format DVD+RW (and various others) */
static unsigned char MMC_FORMAT_UNIT[] = { 0x04, 0x11, 0, 0, 0, 0 };
/* ts A61221 :
To set speed for DVD media (0xBB is for CD but works on my LG GSA drive) */
static unsigned char MMC_SET_STREAMING[] =
{ 0xB6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A61225 :
To obtain write speed descriptors (command can do other things too) */
static unsigned char MMC_GET_PERFORMANCE[] =
{ 0xAC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A70108 : To obtain info about drive and media formatting opportunities */
static unsigned char MMC_READ_FORMAT_CAPACITIES[] =
{ 0x23, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A70205 : To describe the layout of a DVD-R[W] DAO session */
static unsigned char MMC_RESERVE_TRACK[] =
{ 0x53, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A70812 : Read data sectors (for types with 2048 bytes/sector only) */
static unsigned char MMC_READ_10[] =
{ 0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A81210 : Determine the upper limit of readable data size */
static unsigned char MMC_READ_CAPACITY[] =
{ 0x25, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts A90903 : Obtain media type specific information. E.g. manufacturer.
*/
static unsigned char MMC_READ_DISC_STRUCTURE[] =
{ 0xAD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
/* ts B21125 : An alternative to BEh READ CD
*/
static unsigned char MMC_READ_CD_MSF[] =
{ 0xB9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
static int mmc_function_spy_do_tell = 0;
int mmc_function_spy(struct burn_drive *d, char * text)
{
if (mmc_function_spy_do_tell)
fprintf(stderr,"libburn: experimental: mmc_function_spy: %s\n",
text);
if (d == NULL)
return 1;
if (d->drive_role != 1) {
char msg[4096];
sprintf(msg, "Emulated drive caught in SCSI adapter \"%s\"",
text);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002014c,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
d->cancel = 1;
return 0;
}
return 1;
}
int mmc_function_spy_ctrl(int do_tell)
{
mmc_function_spy_do_tell= !!do_tell;
return 1;
}
/* ts A70201 */
int mmc_four_char_to_int(unsigned char *data)
{
return (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
}
/* ts A70201 */
int mmc_int_to_four_char(unsigned char *data, int num)
{
data[0] = (num >> 24) & 0xff;
data[1] = (num >> 16) & 0xff;
data[2] = (num >> 8) & 0xff;
data[3] = num & 0xff;
return 1;
}
static int mmc_start_for_bit0 = 0;
/* @param flag bit0= the calling function should need no START UNIT.
(Handling depends on mmc_start_for_bit0)
*/
int mmc_start_if_needed(struct burn_drive *d, int flag)
{
if (!d->is_stopped)
return 2;
if ((flag & 1) && !mmc_start_for_bit0)
return 2;
d->start_unit(d);
d->is_stopped = 0;
return 1;
}
int mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
{
struct buffer *buf = NULL;
struct command *c;
c = &(d->casual_command);
mmc_start_if_needed(d, 0);
if (mmc_function_spy(d, "mmc_send_cue_sheet") <= 0)
return 0;
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
scsi_init_command(c, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET));
c->retry = 1;
c->page = buf;
c->page->bytes = s->count * 8;
c->page->sectors = 0;
c->opcode[6] = (c->page->bytes >> 16) & 0xFF;
c->opcode[7] = (c->page->bytes >> 8) & 0xFF;
c->opcode[8] = c->page->bytes & 0xFF;
c->dir = TO_DRIVE;
memcpy(c->page->data, s->data, c->page->bytes);
d->issue_command(d, c);
ex:;
BURN_FREE_MEM(buf);
if (c->error) {
d->cancel = 1;
scsi_notify_error(d, c, c->sense, 18, 2);
}
return !c->error;
}
/* ts A70205 : Announce size of a DVD-R[W] DAO session.
@param size The size in bytes to be announced to the drive.
It will get rounded up to align to 32 KiB.
*/
int mmc_reserve_track(struct burn_drive *d, off_t size)
{
struct command *c;
int lba;
char msg[80];
c = &(d->casual_command);
mmc_start_if_needed(d, 0);
if (mmc_function_spy(d, "mmc_reserve_track") <= 0)
return 0;
scsi_init_command(c, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK));
c->retry = 1;
lba = size / 2048;
if (size % 2048)
lba++;
mmc_int_to_four_char(c->opcode+5, lba);
sprintf(msg, "reserving track of %d blocks", lba);
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
msg, 0, 0);
c->page = NULL;
c->dir = NO_TRANSFER;
c->timeout = Libburn_mmc_reserve_timeouT;
d->issue_command(d, c);
if (c->error) {
d->cancel = 1;
scsi_notify_error(d, c, c->sense, 18, 2);
}
return !c->error;
}
/* ts A70201 :
Common track info fetcher for mmc_get_nwa() and mmc_fake_toc()
*/
int mmc_read_track_info(struct burn_drive *d, int trackno, struct buffer *buf,
int alloc_len)
{
struct command *c;
c = &(d->casual_command);
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_track_info") <= 0)
return 0;
scsi_init_command(c, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
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[1] = 1;
if(trackno<=0) {
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
d->current_profile == 0x12 || d->current_profile == 0x42 ||
d->current_profile == 0x43)
/* DVD+RW , DVD-RW restricted overwrite , DVD-RAM
BD-R random recording, BD-RE */
trackno = 1;
else if (d->current_profile == 0x10 ||
d->current_profile == 0x11 ||
d->current_profile == 0x14 ||
d->current_profile == 0x15 ||
d->current_profile == 0x40 ||
d->current_profile == 0x41)
/* DVD-ROM , DVD-R[W] Sequential ,
BD-ROM , BD-R sequential */
trackno = d->last_track_no;
else /* mmc5r03c.pdf: valid only for CD, DVD+R, DVD+R DL */
trackno = 0xFF;
}
mmc_int_to_four_char(c->opcode + 2, trackno);
c->page = buf;
memset(buf->data, 0, BUFFER_SIZE);
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error)
return 0;
return 1;
}
/* ts A61110 : added parameters trackno, lba, nwa. Redefined return value.
@return 1=nwa is valid , 0=nwa is not valid , -1=error */
/* ts A70201 : outsourced 52h READ TRACK INFO command */
int mmc_get_nwa(struct burn_drive *d, int trackno, int *lba, int *nwa)
{
struct buffer *buf = NULL;
int ret, num, alloc_len = 20, err;
unsigned char *data;
char *msg = NULL;
if (trackno <= 0)
d->next_track_damaged = 0;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_get_nwa") <= 0)
{ret = -1; goto ex;}
/* ts B00327 : Avoid to inquire unsuitable media states */
if (d->status != BURN_DISC_BLANK && d->status != BURN_DISC_APPENDABLE)
{ret = 0; goto ex;}
BURN_ALLOC_MEM(buf, struct buffer, 1);
ret = mmc_read_track_info(d, trackno, buf, alloc_len);
if (ret <= 0)
goto ex;
data = buf->data;
*lba = mmc_four_char_to_int(data + 8);
*nwa = mmc_four_char_to_int(data + 12);
num = mmc_four_char_to_int(data + 16);
/* Pioneer BD-RW BDR-205 and LITE-ON LTR-48125S return -150 as *nwa
of blank media */
if (*nwa < *lba && d->status == BURN_DISC_BLANK)
*nwa = *lba;
#ifdef Libburn_pioneer_dvr_216d_load_mode5
/* >>> memorize track mode : data[6] & 0xf */;
#endif
{ static int fake_damage = 0; /* bit0= damage on , bit1= NWA_V off */
if (fake_damage & 1)
data[5] |= 32; /* Damage bit */
if (fake_damage & 2)
data[7] &= ~1;
}
BURN_ALLOC_MEM(msg, char, 160);
if (trackno > 0)
sprintf(msg, "Track number %d: ", trackno);
else
sprintf(msg, "Upcoming track: ");
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
d->current_profile == 0x12 || d->current_profile == 0x43) {
/* overwritable */
*lba = *nwa = num = 0;
} else if (data[5] & 32) { /* ts B10534 : MMC-5 6.27.3.7 Damage Bit */
if (!(data[7] & 1)) { /* NWA_V is set to zero */
/* "not closed due to an incomplete write" */
strcat(msg, "Damaged, not closed and not writable");
err= 0x00020185;
} else {
/* "may be recorded further in an incremental manner"*/
strcat(msg, "Damaged and not closed");
err= 0x00020186;
}
libdax_msgs_submit(libdax_messenger, d->global_index, err,
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
if (trackno <= 0)
d->next_track_damaged |= ((!(data[7] & 1)) << 1) | 1;
{ret = 0; goto ex;}
} else if (!(data[7] & 1)) {
/* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */
strcat(msg, "No Next-Writable-Address");
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020184,
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
if (trackno <= 0)
d->next_track_damaged |= 2;
{ret = 0; goto ex;}
}
if (num > 0) {
burn_drive_set_media_capacity_remaining(d,
((off_t) num) * ((off_t) 2048));
d->media_lba_limit = *nwa + num;
} else
d->media_lba_limit = 0;
/*
fprintf(stderr, "LIBBURN_DEBUG: media_lba_limit= %d\n",
d->media_lba_limit);
*/
ret = 1;
ex:
BURN_FREE_MEM(buf);
BURN_FREE_MEM(msg);
return ret;
}
/* ts A61009 : function is obviously unused. */
/* void mmc_close_disc(struct burn_drive *d, struct burn_write_opts *o) */
void mmc_close_disc(struct burn_write_opts *o)
{
struct burn_drive *d = o->drive;
if (mmc_function_spy(d, "mmc_close_disc") <= 0)
return;
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
"HOW THAT ? mmc_close_disc() was called", 0, 0);
/* ts A61009 : made impossible by removing redundant parameter d */
/* a ssert(o->drive == d); */
o->multi = 0;
spc_select_write_params(d, NULL, 0, o);
mmc_close(d, 1, 0);
}
/* ts A61009 : function is obviously unused. */
/* void mmc_close_session(struct burn_drive *d, struct burn_write_opts *o) */
void mmc_close_session(struct burn_write_opts *o)
{
struct burn_drive *d = o->drive;
if (mmc_function_spy(d, "mmc_close_session") <= 0)
return;
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
"HOW THAT ? mmc_close_session() was called", 0, 0);
/* ts A61009 : made impossible by removing redundant parameter d */
/* a ssert(o->drive == d); */
o->multi = 3;
spc_select_write_params(d, NULL, 0, o);
mmc_close(d, 1, 0);
}
/* ts A70227 : extended meaning of session to address all possible values
of 5Bh CLOSE TRACK SESSION to address any Close Function.
@param session contains the two high bits of Close Function
@param track if not 0: sets the lowest bit of Close Function
*/
void mmc_close(struct burn_drive *d, int session, int track)
{
struct command *c;
char msg[256];
int key, asc, ascq;
c = &(d->casual_command);
if (mmc_function_spy(d, "mmc_close") <= 0)
return;
scsi_init_command(c, MMC_CLOSE, sizeof(MMC_CLOSE));
c->retry = 1;
if (!d->do_no_immed)
c->opcode[1] |= 1; /* ts A70918 : Immed */
/* (ts A61030 : shifted !!session rather than or-ing plain session ) */
c->opcode[2] = ((session & 3) << 1) | !!track;
c->opcode[4] = track >> 8;
c->opcode[5] = track & 0xFF;
c->page = NULL;
c->dir = NO_TRANSFER;
if (d->do_no_immed)
c->timeout = Libburn_mmc_close_noim_timeouT;
else
c->timeout = Libburn_mmc_close_timeouT;
d->issue_command(d, c);
/* ts A70918 : Immed : wait for drive to complete command */
if (c->error) {
sprintf(msg, "Failed to close %s (%d)",
session > 1 ? "disc" : session > 0 ? "session" : "track",
((session & 3) << 1) | !!track);
sprintf(msg + strlen(msg), ". SCSI error : ");
scsi_error_msg(d, c->sense, 14, msg + strlen(msg),
&key, &asc, &ascq);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002017e,
LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
d->cancel = 1;
return;
}
spc_human_readable_cmd(c, msg, 160, 0);
if (spc_wait_unit_attention(d, 3600, msg, 0) <= 0)
d->cancel = 1;
}
void mmc_get_event(struct burn_drive *d)
{
struct buffer *buf = NULL;
struct command *c;
int alloc_len = 8, len, evt_code, loops = 0;
unsigned char *evt;
c = &(d->casual_command);
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
if (mmc_function_spy(d, "mmc_get_event") <= 0)
goto ex;
again:;
scsi_init_command(c, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
c->dxfer_len = 8;
/* >>> have a burn_drive element for Notification Class */;
c->opcode[4] = 0x7e;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error)
goto ex;
evt = c->page->data;
len = ((evt[0] << 8) | evt[1]) + 2;
if (len < 8)
goto ex;
/* >>> memorize evt[3] in burn_drive element for Notification Class */;
if (evt[3] == 0) /* No event */
goto ex;
evt_code = evt[4] & 0xf;
if (evt_code == 0) /* No change */
goto ex;
switch (evt[2] & 7) {
case 0: /* no events supported */
goto ex;
case 1: /* Operational change */
if (((evt[6] << 8) | evt[7])) {
alloc_len = 8;
mmc_get_configuration_al(d, &alloc_len);
}
break;
case 2: /* Power Management */
if (evt[5] >= 2)
d->start_unit(d);
break;
case 3: /* External request */
/* >>> report about external request */;
break;
case 4: /* Media */
if (evt_code == 2) {
d->start_unit(d);
alloc_len = 8;
mmc_get_configuration_al(d, &alloc_len);
}
break;
case 5: /* Multiple Host Events */
/* >>> report about foreign host interference */;
break;
case 6: /* Device busy */
if (evt_code == 1 && evt[5]) {
/* >>> wait the time announced in evt[6],[7]
as 100ms units */;
}
break;
default: /* reserved */
break;
}
loops++;
if (loops < 100)
goto again;
ex:;
BURN_FREE_MEM(buf);
}
/* ts A70711
This has become a little monster because of the creative buffer reports of
my LG GSA-4082B : Belated, possibly statistically dampened. But only with
DVD media. With CD it is ok.
*/
static int mmc_wait_for_buffer_free(struct burn_drive *d, struct buffer *buf)
{
int usec= 0, need, reported_3s = 0, first_wait = 1;
struct timeval t0,tnow;
double max_fac, min_fac, waiting;
/* Enable to get reported waiting activities and total time.
#define Libburn_mmc_wfb_debuG 1
*/
#ifdef Libburn_mmc_wfb_debuG
char sleeplist[32768];
static int buffer_still_invalid = 1;
#endif
max_fac = ((double) d->wfb_max_percent) / 100.0;
/* Buffer info from the drive is valid only after writing has begun.
Caring for buffer space makes sense mostly after max_percent of the
buffer was transmitted. */
if (d->progress.buffered_bytes <= 0 ||
d->progress.buffer_capacity <= 0 ||
d->progress.buffered_bytes + buf->bytes <=
d->progress.buffer_capacity * max_fac)
return 2;
#ifdef Libburn_mmc_wfb_debuG
if (buffer_still_invalid)
fprintf(stderr,
"\nLIBBURN_DEBUG: Buffer considered valid now\n");
buffer_still_invalid = 0;
#endif
/* The pessimistic counter does not assume any buffer consumption */
if (d->pessimistic_buffer_free - buf->bytes >=
( 1.0 - max_fac) * d->progress.buffer_capacity)
return 1;
/* There is need to inquire the buffer fill */
d->pessimistic_writes++;
min_fac = ((double) d->wfb_min_percent) / 100.0;
gettimeofday(&t0, NULL);
#ifdef Libburn_mmc_wfb_debuG
sleeplist[0]= 0;
sprintf(sleeplist,"(%d%s %d)",
(int) (d->pessimistic_buffer_free - buf->bytes),
(d->pbf_altered ? "? -" : " -"),
(int) ((1.0 - max_fac) * d->progress.buffer_capacity));
#endif
while (1) {
if ((!first_wait) || d->pbf_altered) {
d->pbf_altered = 1;
mmc_read_buffer_capacity(d);
}
#ifdef Libburn_mmc_wfb_debuG
if(strlen(sleeplist) < sizeof(sleeplist) - 80)
sprintf(sleeplist+strlen(sleeplist)," (%d%s %d)",
(int) (d->pessimistic_buffer_free - buf->bytes),
(d->pbf_altered ? "? -" : " -"),
(int) ((1.0 - min_fac) * d->progress.buffer_capacity));
#endif
gettimeofday(&tnow, NULL);
waiting = (tnow.tv_sec - t0.tv_sec) +
((double) (tnow.tv_usec - t0.tv_usec)) / 1.0e6;
if (d->pessimistic_buffer_free - buf->bytes >=
(1.0 - min_fac) * d->progress.buffer_capacity) {
#ifdef Libburn_mmc_wfb_debuG
if(strlen(sleeplist) >= sizeof(sleeplist) - 80)
strcat(sleeplist," ...");
sprintf(sleeplist+strlen(sleeplist)," -> %d [%.6f]",
(int) (
d->pessimistic_buffer_free - buf->bytes -
(1.0 - min_fac) * d->progress.buffer_capacity
), waiting);
fprintf(stderr,
"\nLIBBURN_DEBUG: sleeplist= %s\n",sleeplist);
#endif
return 1;
}
/* Waiting is needed */
if (waiting >= 3 && !reported_3s) {
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002013d,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_LOW,
"Waiting for free buffer takes more than 3 seconds",
0,0);
reported_3s = 1;
} else if (d->wfb_timeout_sec > 0 &&
waiting > d->wfb_timeout_sec) {
d->wait_for_buffer_free = 0;
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002013d,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
"Timeout with waiting for free buffer. Now disabled.",
0,0);
break;
}
need = (1.0 - min_fac) * d->progress.buffer_capacity +
buf->bytes - d->pessimistic_buffer_free;
usec = 0;
if (d->nominal_write_speed > 0)
usec = ((double) need) / 1000.0 /
((double) d->nominal_write_speed) * 1.0e6;
else
usec = d->wfb_min_usec * 2;
/* >>> learn about buffer progress and adjust usec */
if (usec < (int) d->wfb_min_usec)
usec = d->wfb_min_usec;
else if (usec > (int) d->wfb_max_usec)
usec = d->wfb_max_usec;
usleep(usec);
if (d->waited_usec < 0xf0000000)
d->waited_usec += usec;
d->waited_tries++;
if(first_wait)
d->waited_writes++;
#ifdef Libburn_mmc_wfb_debuG
if(strlen(sleeplist) < sizeof(sleeplist) - 80)
sprintf(sleeplist+strlen(sleeplist)," %d", usec);
#endif
first_wait = 0;
}
return 0;
}
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
{
struct command *c;
int len;
c = &(d->casual_command);
mmc_start_if_needed(d, 0);
if (mmc_function_spy(d, "mmc_write_12") <= 0)
return;
len = buf->sectors;
scsi_init_command(c, MMC_WRITE_12, sizeof(MMC_WRITE_12));
c->retry = 1;
mmc_int_to_four_char(c->opcode + 2, start);
mmc_int_to_four_char(c->opcode + 6, len);
c->page = buf;
c->dir = TO_DRIVE;
c->timeout = Libburn_scsi_write_timeouT;
d->issue_command(d, c);
/* ts A70711 */
d->pessimistic_buffer_free -= buf->bytes;
d->pbf_altered = 1;
}
#ifdef Libburn_write_time_debuG
static int print_time(int flag)
{
static struct timeval prev = {0, 0};
struct timeval now;
int ret, diff;
ret = gettimeofday(&now, NULL);
if (ret == -1)
return 0;
if (now.tv_sec - prev.tv_sec < Libburn_scsi_write_timeouT) {
diff = (now.tv_sec - prev.tv_sec) * 1000000 +
((int) (now.tv_usec) - (int) prev.tv_usec);
fprintf(stderr, "\nlibburn_DEBUG: %d.%-6d : %d\n", (int) now.tv_sec, (int) now.tv_usec, diff);
}
memcpy(&prev, &now, sizeof(struct timeval));
return 1;
}
#endif /* Libburn_write_time_debuG */
int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
{
int cancelled;
struct command *c;
int len, key, asc, ascq;
char *msg = NULL;
#ifdef Libburn_write_time_debuG
extern int burn_sg_log_scsi;
#endif
/*
fprintf(stderr, "libburn_DEBUG: buffer sectors= %d bytes= %d\n",
buf->sectors, buf->bytes);
*/
c = &(d->casual_command);
#ifdef Libburn_log_in_and_out_streaM
/* <<< ts A61031 */
static int tee_fd= -1;
if(tee_fd==-1)
tee_fd= open("/tmp/libburn_sg_written",
O_WRONLY | O_CREAT | O_TRUNC | O_BINARY,
S_IRUSR | S_IWUSR);
#endif /* Libburn_log_in_and_out_streaM */
mmc_start_if_needed(d, 0);
if (mmc_function_spy(d, "mmc_write") <= 0)
return BE_CANCELLED;
cancelled = d->cancel;
if (cancelled)
return BE_CANCELLED;
/* ts A70215 */
if (d->media_lba_limit > 0 && start >= d->media_lba_limit) {
msg = calloc(1, 320);
if (msg != NULL) {
sprintf(msg,
"Exceeding range of permissible write addresses (%d >= %d)",
start, d->media_lba_limit);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002012d,
LIBDAX_MSGS_SEV_FATAL, 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, 0x0002012d,
LIBDAX_MSGS_SEV_FATAL,
LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
}
free(msg);
}
d->cancel = 1; /* No need for mutexing because atomic */
return BE_CANCELLED;
}
len = buf->sectors;
/* ts A61009 : buffer fill problems are to be handled by caller */
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
/* ts A70711 */
if(d->wait_for_buffer_free)
mmc_wait_for_buffer_free(d, buf);
#ifdef Libburn_write_time_debuG
if (burn_sg_log_scsi & 3)
print_time(0);
#endif
/* ts A80412 */
if(d->do_stream_recording > 0 && start >= d->stream_recording_start) {
scsi_init_command(c, MMC_WRITE_12, sizeof(MMC_WRITE_12));
mmc_int_to_four_char(c->opcode + 2, start);
mmc_int_to_four_char(c->opcode + 6, len);
c->opcode[10] = 1<<7; /* Streaming bit */
} else {
scsi_init_command(c, MMC_WRITE_10, sizeof(MMC_WRITE_10));
mmc_int_to_four_char(c->opcode + 2, start);
c->opcode[6] = 0;
c->opcode[7] = (len >> 8) & 0xFF;
c->opcode[8] = len & 0xFF;
}
c->retry = 1;
c->page = buf;
c->dir = TO_DRIVE;
c->timeout = Libburn_scsi_write_timeouT;
#ifdef Libburn_log_in_and_out_streaM
/* <<< ts A61031 */
if(tee_fd!=-1) {
write(tee_fd, c->page->data, c->page->bytes);
}
#endif /* Libburn_log_in_and_out_streaM */
d->issue_command(d, c);
/* ts A70711 */
d->pessimistic_buffer_free -= buf->bytes;
d->pbf_altered = 1;
/* ts A61112 : react on eventual error condition */
spc_decode_sense(c->sense, 0, &key, &asc, &ascq);
if (c->error && key != 0) {
int key, asc, ascq;
int err_sev = LIBDAX_MSGS_SEV_FATAL;
msg = calloc(1, 320);
if (msg != NULL) {
sprintf(msg, "SCSI error on write(%d,%d): ",
start, len);
scsi_error_msg(d, c->sense, 14, msg + strlen(msg),
&key, &asc, &ascq);
}
/* ts B31023 */
/* Memorize if on DVD-RW write mode is TAO/Incremental and
error [5 64 00] occurs within the first drive buffer fill.
*/
if (d->current_profile == 0x14 && d->write_opts != NULL &&
(d->progress.buffer_capacity == 0 ||
start < (int) d->progress.buffer_capacity / 2048) &&
key == 5 && asc == 0x64 && ascq == 0) {
if (d->write_opts->write_type == BURN_WRITE_TAO) {
d->was_feat21h_failure = 1 + (start == 0);
if (d->write_opts->feat21h_fail_sev != 0)
err_sev =
d->write_opts->feat21h_fail_sev;
}
}
if (msg != NULL) {
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002011d,
err_sev, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
strcpy(msg, "CDB= ");
if (spc_human_readable_cmd(c, msg + strlen(msg),
320 - strlen(msg), 0) > 0) {
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x0002011d,
err_sev, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
}
free(msg);
}
d->cancel = 1;
return BE_CANCELLED;
}
d->write_retry_count += c->retry_count;
return 0;
}
/* ts A70201 : Set up an entry for mmc_fake_toc() */
int mmc_fake_toc_entry(struct burn_toc_entry *entry, int session_number,
int track_number,
unsigned char *size_data, unsigned char *start_data,
unsigned char *last_adr_data)
{
int min, sec, frames, num;
/* mark DVD extensions and Track Info extension as valid */
entry->extensions_valid |= (1 | 2);
/* defaults are as of mmc5r03.pdf 6.26.3.2.4 Fabricated TOC */
entry->session = session_number & 0xff;
entry->session_msb = (session_number >> 8) & 0xff;
entry->adr = 1;
entry->control = 4;
entry->tno = 0;
entry->point = track_number & 0xff;
entry->point_msb = (track_number >> 8) & 0xff;
num = mmc_four_char_to_int(size_data);
entry->track_blocks = num;
burn_lba_to_msf(num, &min, &sec, &frames);
if (min > 255) {
min = 255;
sec = 255;
frames = 255;
}
entry->min = min;
entry->sec = sec;
entry->frame = frames;
entry->zero = 0;
num = mmc_four_char_to_int(start_data);
entry->start_lba = num;
burn_lba_to_msf(num, &min, &sec, &frames);
if (min > 255) {
min = 255;
sec = 255;
frames = 255;
}
entry->pmin = min;
entry->psec = sec;
entry->pframe = frames;
entry->last_recorded_address = mmc_four_char_to_int(last_adr_data);
return 1;
}
/* ts A71128 : for DVD-ROM drives which offer no reliable track information */
static int mmc_read_toc_fmt0_al(struct burn_drive *d, int *alloc_len)
{
struct burn_track *track;
struct burn_session *session;
struct burn_toc_entry *entry;
struct buffer *buf = NULL;
struct command *c = NULL;
int dlen, i, old_alloc_len, session_number, prev_session = -1, ret;
int lba, size;
unsigned char *tdata, size_data[4], start_data[4], end_data[4];
if (*alloc_len < 4)
{ret = 0; goto ex;}
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
scsi_init_command(c, MMC_GET_TOC_FMT0, sizeof(MMC_GET_TOC_FMT0));
c->dxfer_len = *alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error) {
err_ex:;
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002010d,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
"Could not inquire TOC", 0,0);
d->status = BURN_DISC_UNSUITABLE;
d->toc_entries = 0;
/* Preferring memory leaks over fandangos */
d->toc_entry = calloc(1, sizeof(struct burn_toc_entry));
{ret = 0; goto ex;}
}
dlen = c->page->data[0] * 256 + c->page->data[1];
old_alloc_len = *alloc_len;
*alloc_len = dlen + 2;
if (old_alloc_len < 12)
{ret = 1; goto ex;}
if (dlen + 2 > old_alloc_len)
dlen = old_alloc_len - 2;
d->complete_sessions = 1 + c->page->data[3] - c->page->data[2];
#ifdef Libburn_disc_with_incomplete_sessioN
/* ts B30112 : number of open sessions */
d->incomplete_sessions = 0;
#endif
d->last_track_no = d->complete_sessions;
if (dlen - 2 < (d->last_track_no + 1) * 8) {
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020159,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
"TOC Format 0 returns inconsistent data", 0,0);
goto err_ex;
}
d->toc_entries = d->last_track_no + d->complete_sessions;
if (d->toc_entries < 1)
{ret = 0; goto ex;}
d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry));
if(d->toc_entry == NULL)
{ret = 0; goto ex;}
d->disc = burn_disc_create();
if (d->disc == NULL)
{ret = 0; goto ex;}
for (i = 0; i < d->complete_sessions; i++) {
session = burn_session_create();
if (session == NULL)
{ret = 0; goto ex;}
burn_disc_add_session(d->disc, session, BURN_POS_END);
burn_session_free(session);
}
for (i = 0; i < d->last_track_no; i++) {
tdata = c->page->data + 4 + i * 8;
session_number = i + 1;
if (session_number != prev_session && prev_session > 0) {
/* leadout entry previous session */
entry = &(d->toc_entry[(i - 1) + prev_session]);
lba = mmc_four_char_to_int(start_data) +
mmc_four_char_to_int(size_data);
mmc_int_to_four_char(start_data, lba);
mmc_int_to_four_char(size_data, 0);
mmc_int_to_four_char(end_data, lba - 1);
mmc_fake_toc_entry(entry, prev_session, 0xA2,
size_data, start_data, end_data);
entry->min= entry->sec= entry->frame= 0;
d->disc->session[prev_session - 1]->leadout_entry =
entry;
}
/* ??? >>> d->media_capacity_remaining , d->media_lba_limit
as of mmc_fake_toc()
*/
entry = &(d->toc_entry[i + session_number - 1]);
track = burn_track_create();
if (track == NULL)
{ret = -1; goto ex;}
burn_session_add_track(
d->disc->session[session_number - 1],
track, BURN_POS_END);
track->entry = entry;
burn_track_free(track);
memcpy(start_data, tdata + 4, 4);
/* size_data are estimated from next track start */
memcpy(size_data, tdata + 8 + 4, 4);
mmc_int_to_four_char(end_data,
mmc_four_char_to_int(size_data) - 1);
size = mmc_four_char_to_int(size_data) -
mmc_four_char_to_int(start_data);
mmc_int_to_four_char(size_data, size);
mmc_fake_toc_entry(entry, session_number, i + 1,
size_data, start_data, end_data);
if (prev_session != session_number)
d->disc->session[session_number - 1]->firsttrack = i+1;
d->disc->session[session_number - 1]->lasttrack = i+1;
prev_session = session_number;
}
if (prev_session > 0 && prev_session <= d->disc->sessions) {
/* leadout entry of last session of closed disc */
tdata = c->page->data + 4 + d->last_track_no * 8;
entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]);
memcpy(start_data, tdata + 4, 4);
mmc_int_to_four_char(size_data, 0);
mmc_int_to_four_char(end_data,
mmc_four_char_to_int(start_data) - 1);
mmc_fake_toc_entry(entry, prev_session, 0xA2,
size_data, start_data, end_data);
entry->min= entry->sec= entry->frame= 0;
d->disc->session[prev_session - 1]->leadout_entry = entry;
}
ret = 1;
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
return ret;
}
/* ts A71128 : for DVD-ROM drives which offer no reliable track information */
static int mmc_read_toc_fmt0(struct burn_drive *d)
{
int alloc_len = 4, ret;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_toc_fmt0") <= 0)
return -1;
ret = mmc_read_toc_fmt0_al(d, &alloc_len);
if (alloc_len >= 12)
ret = mmc_read_toc_fmt0_al(d, &alloc_len);
return ret;
}
/* ts A70131 : compose a disc TOC structure from d->complete_sessions
and 52h READ TRACK INFORMATION */
int mmc_fake_toc(struct burn_drive *d)
{
struct burn_track *track;
struct burn_session *session;
struct burn_toc_entry *entry;
struct buffer *buf = NULL;
int i, session_number, prev_session = -1, ret, lba, alloc_len = 34;
unsigned char *tdata, size_data[4], start_data[4], end_data[4];
char *msg = NULL;
if (mmc_function_spy(d, "mmc_fake_toc") <= 0)
{ret = -1; goto ex;}
BURN_ALLOC_MEM(buf, struct buffer, 1);
#ifdef Libburn_disc_with_incomplete_sessioN
if (d->last_track_no <= 0 ||
d->complete_sessions + d->incomplete_sessions <= 0 ||
d->status == BURN_DISC_BLANK)
{ret = 2; goto ex;}
#else
if (d->last_track_no <= 0 || d->complete_sessions <= 0 ||
d->status == BURN_DISC_BLANK)
{ret = 2; goto ex;}
#endif /* ! Libburn_disc_with_incomplete_sessioN */
if (d->last_track_no > BURN_MMC_FAKE_TOC_MAX_SIZE) {
msg = calloc(1, 160);
if (msg != NULL) {
sprintf(msg,
"Too many logical tracks recorded (%d , max. %d)\n",
d->last_track_no, BURN_MMC_FAKE_TOC_MAX_SIZE);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002012c,
LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH,
msg, 0,0);
free(msg);
}
{ret = 0; goto ex;}
}
/* ts A71128 : My DVD-ROM drive issues no reliable track info.
One has to try 43h READ TOC/PMA/ATIP Form 0. */
if ((d->current_profile == 0x10) && d->last_track_no <= 1) {
ret = mmc_read_toc_fmt0(d);
goto ex;
}
d->disc = burn_disc_create();
if (d->disc == NULL)
{ret = -1; goto ex;}
d->toc_entries = d->last_track_no
+ d->complete_sessions + d->incomplete_sessions;
d->toc_entry = calloc(d->toc_entries, sizeof(struct burn_toc_entry));
if (d->toc_entry == NULL)
{ret = -1; goto ex;}
memset(d->toc_entry, 0,d->toc_entries * sizeof(struct burn_toc_entry));
#ifdef Libburn_disc_with_incomplete_sessioN
for (i = 0; i < d->complete_sessions + d->incomplete_sessions; i++) {
#else
for (i = 0; i < d->complete_sessions; i++) {
#endif
session = burn_session_create();
if (session == NULL)
{ret = -1; goto ex;}
burn_disc_add_session(d->disc, session, BURN_POS_END);
burn_session_free(session);
}
#ifdef Libburn_disc_with_incomplete_sessioN
d->disc->incomplete_sessions = d->incomplete_sessions;
#endif
memset(size_data, 0, 4);
memset(start_data, 0, 4);
/* Entry Layout :
session 1 track 1 entry 0
...
session 1 track N entry N-1
leadout 1 entry N
session 2 track N+1 entry N+1
...
session 2 track M+1 entry M+1
leadout 2 entry M+2
session X track K entry (K-1)+(X-1)
...
session X track i+1 entry i+(X-1)
leadout X entry i+X
*/
for (i = 0; i < d->last_track_no; i++) {
ret = mmc_read_track_info(d, i+1, buf, alloc_len);
if (ret <= 0)
goto ex;
tdata = buf->data;
session_number = (tdata[33] << 8) | tdata[3];
if (session_number <= 0)
continue;
if (session_number != prev_session && prev_session > 0) {
/* leadout entry previous session */
entry = &(d->toc_entry[(i - 1) + prev_session]);
lba = mmc_four_char_to_int(start_data) +
mmc_four_char_to_int(size_data);
mmc_int_to_four_char(start_data, lba);
mmc_int_to_four_char(size_data, 0);
mmc_int_to_four_char(end_data, lba - 1);
mmc_fake_toc_entry(entry, prev_session, 0xA2,
size_data, start_data, end_data);
entry->min= entry->sec= entry->frame= 0;
d->disc->session[prev_session - 1]->leadout_entry =
entry;
}
#ifdef Libburn_disc_with_incomplete_sessioN
if (session_number > d->complete_sessions) {
#else
if (session_number > d->disc->sessions) {
#endif
if (i == d->last_track_no - 1) {
/* ts A70212 : Last track field Free Blocks */
burn_drive_set_media_capacity_remaining(d,
((off_t) mmc_four_char_to_int(tdata + 16)) *
((off_t) 2048));
d->media_lba_limit = 0;
}
#ifdef Libburn_disc_with_incomplete_sessioN
if (session_number > d->disc->sessions )
continue;
#else
continue;
#endif
}
entry = &(d->toc_entry[i + session_number - 1]);
track = burn_track_create();
if (track == NULL)
{ret = -1; goto ex;}
burn_session_add_track(
d->disc->session[session_number - 1],
track, BURN_POS_END);
track->entry = entry;
burn_track_free(track);
memcpy(size_data, tdata + 24, 4);
memcpy(start_data, tdata + 8, 4);
memcpy(end_data, tdata + 28, 4);
mmc_fake_toc_entry(entry, session_number, i + 1,
size_data, start_data, end_data);
entry->track_status_bits = tdata[5] | (tdata[6] << 8) |
(tdata[7] << 16);
entry->extensions_valid |= 4;
if (prev_session != session_number)
d->disc->session[session_number - 1]->firsttrack = i+1;
d->disc->session[session_number - 1]->lasttrack = i+1;
prev_session = session_number;
}
if (prev_session > 0 && prev_session <= d->disc->sessions) {
/* leadout entry of last session of closed disc */
entry = &(d->toc_entry[(d->last_track_no - 1) + prev_session]);
lba = mmc_four_char_to_int(start_data) +
mmc_four_char_to_int(size_data);
mmc_int_to_four_char(start_data, lba);
mmc_int_to_four_char(size_data, 0);
mmc_int_to_four_char(end_data, lba - 1);
mmc_fake_toc_entry(entry, prev_session, 0xA2,
size_data, start_data, end_data);
entry->min= entry->sec= entry->frame= 0;
d->disc->session[prev_session - 1]->leadout_entry = entry;
}
ret = 1;
ex:;
BURN_FREE_MEM(buf);
return ret;
}
static int mmc_register_leadout(struct burn_drive *d, int *highest_leadout,
int toc_idx)
{
int lba;
lba = burn_msf_to_lba(d->toc_entry[toc_idx].pmin,
d->toc_entry[toc_idx].psec,
d->toc_entry[toc_idx].pframe);
if (lba > *highest_leadout)
*highest_leadout = lba;
return 1;
}
static int mmc_read_toc_al(struct burn_drive *d, int *alloc_len)
{
/* read full toc, all sessions, in m/s/f form, 4k buffer */
/* ts A70201 : or fake a toc from track information */
struct burn_track *track;
struct burn_session *session;
struct buffer *buf = NULL;
struct command *c = NULL;
int dlen;
int i, old_alloc_len, t_idx, ret, highest_leadout = -1;
unsigned char *tdata;
char *msg = NULL;
if (*alloc_len < 4)
{ret = 0; goto ex;}
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
BURN_ALLOC_MEM(msg, char, 321);
if (!(d->current_profile == -1 || d->current_is_cd_profile)) {
/* ts A70131 : MMC_GET_TOC uses Response Format 2
For DVD this fails with 5,24,00 */
/* mmc_read_toc_fmt0() uses
Response Format 0: mmc5r03.pdf 6.26.3.2
which does not yield the same result with the same disc
on different drives.
*/
/* ts A70201 :
This uses the session count from 51h READ DISC INFORMATION
and the track records from 52h READ TRACK INFORMATION.
mmc_read_toc_fmt0() is used as fallback for dull DVD-ROM.
*/
mmc_fake_toc(d);
if (d->status == BURN_DISC_UNREADY)
d->status = BURN_DISC_FULL;
{ret = 1; goto ex;}
}
/* ts A90823:
SanDisk Cruzer U3 memory stick stalls on format 2.
Format 0 seems to be more conservative with read-only drives.
*/
if (!((d->mdata->p2a_valid > 0 && d->mdata->cdrw_write) ||
d->current_profile != 0x08)) {
ret = mmc_read_toc_fmt0(d);
goto ex;
}
scsi_init_command(c, MMC_GET_TOC, sizeof(MMC_GET_TOC));
c->dxfer_len = *alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error) {
/* ts A61020 : this snaps on non-blank DVD media */
/* ts A61106 : also snaps on CD with unclosed track/session */
/* Very unsure whether this old measure is ok.
Obviously higher levels do not care about this.
outdated info: DVD+RW burns go on after passing through here.
d->busy = BURN_DRIVE_IDLE;
*/
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002010d,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH,
"Could not inquire TOC", 0,0);
d->status = BURN_DISC_UNSUITABLE;
d->toc_entries = 0;
/* Preferring memory leaks over fandangos */
d->toc_entry = calloc(1, sizeof(struct burn_toc_entry));
{ret = 0; goto ex;}
}
dlen = c->page->data[0] * 256 + c->page->data[1];
old_alloc_len = *alloc_len;
*alloc_len = dlen + 2;
if (old_alloc_len < 15)
{ret = 1; goto ex;}
if (dlen + 2 > old_alloc_len)
dlen = old_alloc_len - 2;
d->toc_entries = (dlen - 2) / 11;
if (d->toc_entries < 1)
{ret = 0; goto ex;}
/*
some drives fail this check.
ts A61007 : if re-enabled then not via Assert.
a ssert(((dlen - 2) % 11) == 0);
*/
/* ts A81202: plus number of sessions as reserve for leadout default */
d->toc_entry = calloc(d->toc_entries + (unsigned char) c->page->data[3],
sizeof(struct burn_toc_entry));
if(d->toc_entry == NULL) /* ts A70825 */
{ret = 0; goto ex;}
tdata = c->page->data + 4;
d->disc = burn_disc_create();
if (d->disc == NULL) /* ts A70825 */
{ret = 0; goto ex;}
for (i = 0; i < c->page->data[3]; i++) {
session = burn_session_create();
if (session == NULL) /* ts A70825 */
{ret = 0; goto ex;}
burn_disc_add_session(d->disc, session, BURN_POS_END);
burn_session_free(session);
}
/* ts A61022 */
for (i = 0; i < d->toc_entries; i++, tdata += 11) {
/*
fprintf(stderr, "libburn_experimental: toc entry #%d : %d %d %d\n",i,tdata[8], tdata[9], tdata[10]);
*/
#ifdef Libburn_allow_first_hiddeN
/* ts B00430 : this causes problems because the track has
no entry. One would have to coordinate this
with other parts of libburn.
*/
if (tdata[3] == 1) {
if (burn_msf_to_lba(tdata[8], tdata[9], tdata[10])) {
d->disc->session[0]->hidefirst = 1;
track = burn_track_create();
burn_session_add_track(d->disc->
session[tdata[0] - 1],
track, BURN_POS_END);
burn_track_free(track);
}
}
#endif /* Libburn_allow_first_hiddeN */
if (tdata[0] <= 0 || tdata[0] > d->disc->sessions)
tdata[0] = d->disc->sessions;
if (tdata[3] < 100 && tdata[0] > 0) {
track = burn_track_create();
burn_session_add_track(d->disc->session[tdata[0] - 1],
track, BURN_POS_END);
track->entry = &d->toc_entry[i];
burn_track_free(track);
}
d->toc_entry[i].session = tdata[0];
d->toc_entry[i].adr = tdata[1] >> 4;
d->toc_entry[i].control = tdata[1] & 0xF;
d->toc_entry[i].tno = tdata[2];
d->toc_entry[i].point = tdata[3];
d->toc_entry[i].min = tdata[4];
d->toc_entry[i].sec = tdata[5];
d->toc_entry[i].frame = tdata[6];
d->toc_entry[i].zero = tdata[7];
d->toc_entry[i].pmin = tdata[8];
d->toc_entry[i].psec = tdata[9];
d->toc_entry[i].pframe = tdata[10];
if (tdata[3] == 0xA0)
d->disc->session[tdata[0] - 1]->firsttrack = tdata[8];
if (tdata[3] == 0xA1)
d->disc->session[tdata[0] - 1]->lasttrack = tdata[8];
if (tdata[3] == 0xA2) {
d->disc->session[tdata[0] - 1]->leadout_entry =
&d->toc_entry[i];
/* ts B60305 */
mmc_register_leadout(d, &highest_leadout, i);
}
}
/* ts A70131 : was (d->status != BURN_DISC_BLANK) */
if (d->status == BURN_DISC_UNREADY)
d->status = BURN_DISC_FULL;
toc_find_modes(d);
/* ts A81202 ticket 146 : a drive reported a session with no leadout */
for (i = 0; i < d->disc->sessions; i++) {
if (d->disc->session[i]->leadout_entry != NULL)
continue;
sprintf(msg, "Session %d of %d encountered without leadout",
i + 1, d->disc->sessions);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020160,
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
/* Produce default leadout entry from last track of session
which will thus get its size set to 0 */;
if (d->disc->session[i]->track != NULL &&
d->disc->session[i]->tracks > 0) {
t_idx = d->toc_entries++;
memcpy(d->toc_entry + t_idx,
d->disc->session[i]->track[
d->disc->session[i]->tracks - 1]->entry,
sizeof(struct burn_toc_entry));
d->toc_entry[t_idx].point = 0xA2;
d->disc->session[i]->leadout_entry =
d->toc_entry + t_idx;
} else {
burn_disc_remove_session(d->disc, d->disc->session[i]);
sprintf(msg,
"Empty session %d deleted. Now %d sessions.",
i + 1, d->disc->sessions);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020161,
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
i--;
}
}
/* A80808 */
burn_disc_cd_toc_extensions(d, 0);
/* ts B60304
Most drives report READ CAPACITY of TAO CD too high by 2 blocks.
TOC format 2 always reports 2 blocks more than are readable.
So here it is possible to check and mark as trusted.
*/
if (highest_leadout > 0 && d->media_read_capacity != 0x7fffffff &&
!d->mr_capacity_trusted) {
if (highest_leadout - 3 == d->media_read_capacity) {
d->mr_capacity_trusted = 1;
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00000002, LIBDAX_MSGS_SEV_DEBUG,
LIBDAX_MSGS_PRIO_ZERO,
"Trusting READ CAPACITY by 2 extra blocks in TOC. Assuming TAO.",
0, 0);
}
}
ret = 1;
ex:;
BURN_FREE_MEM(msg);
BURN_FREE_MEM(c);
BURN_FREE_MEM(buf);
return ret;
}
void mmc_read_toc(struct burn_drive *d)
{
int alloc_len = 4, ret;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_toc") <= 0)
return;
ret = mmc_read_toc_al(d, &alloc_len);
/*
fprintf(stderr,
"LIBBURN_DEBUG: 43h READ TOC alloc_len = %d , ret = %d\n",
alloc_len, ret);
*/
if (alloc_len >= 15)
ret = mmc_read_toc_al(d, &alloc_len);
if (ret <= 0)
return;
}
/* ts A70131 : This tries to get the start of the last complete session */
/* man mkisofs , option -C :
The first number is the sector number of the first sector in
the last session of the disk that should be appended to.
*/
int mmc_read_multi_session_c1(struct burn_drive *d, int *trackno, int *start)
{
struct buffer *buf = NULL;
struct command *c = NULL;
unsigned char *tdata;
int num_sessions, session_no, num_tracks, alloc_len = 12, ret;
struct burn_disc *disc;
struct burn_session **sessions;
struct burn_track **tracks;
struct burn_toc_entry toc_entry;
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_multi_session_c1") <= 0)
{ret = 0; goto ex;}
/* First try to evaluate the possibly loaded TOC before issuing
a MMC command. This search obtains the first track of the last
complete session which has a track.
*/
*trackno = 0;
disc = burn_drive_get_disc(d);
if (disc == NULL)
goto inquire_drive;
sessions = burn_disc_get_sessions(disc, &num_sessions);
for (session_no = 0; session_no<num_sessions; session_no++) {
tracks = burn_session_get_tracks(sessions[session_no],
&num_tracks);
if (tracks == NULL || num_tracks <= 0)
continue;
burn_track_get_entry(tracks[0], &toc_entry);
if (toc_entry.extensions_valid & 1) { /* DVD extension valid */
*start = toc_entry.start_lba;
*trackno = (toc_entry.point_msb << 8)| toc_entry.point;
} else {
*start = burn_msf_to_lba(toc_entry.pmin,
toc_entry.psec, toc_entry.pframe);
*trackno = toc_entry.point;
}
}
burn_disc_free(disc);
if(*trackno > 0)
{ret = 1; goto ex;}
inquire_drive:;
/* mmc5r03.pdf 6.26.3.3.3 states that with non-CD this would
be a useless fake always starting at track 1, lba 0.
My drives return useful data, though.
MMC-3 states that DVD had no tracks. So maybe this mandatory fake
is a forgotten legacy ?
*/
scsi_init_command(c, MMC_GET_MSINFO, sizeof(MMC_GET_MSINFO));
c->dxfer_len = alloc_len;
c->opcode[7]= (c->dxfer_len >> 8) & 0xff;
c->opcode[8]= c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error)
{ret = 0; goto ex;}
tdata = c->page->data + 4;
*trackno = tdata[2];
*start = mmc_four_char_to_int(tdata + 4);
ret = 1;
ex:;
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
return ret;
}
/* ts A61201 */
char *mmc_obtain_profile_name(int profile_number)
{
static char *texts[0x53] = {NULL};
int i, max_pno = 0x53;
if (texts[0] == NULL) {
for (i = 0; i<max_pno; i++)
texts[i] = "";
/* mmc5r03c.pdf , Table 89, Spelling: guessed cdrecord style */
texts[0x01] = "Non-removable disk";
texts[0x02] = "Removable disk";
texts[0x03] = "MO erasable";
texts[0x04] = "Optical write once";
texts[0x05] = "AS-MO";
texts[0x08] = "CD-ROM";
texts[0x09] = "CD-R";
texts[0x0a] = "CD-RW";
texts[0x10] = "DVD-ROM";
texts[0x11] = "DVD-R sequential recording";
texts[0x12] = "DVD-RAM";
texts[0x13] = "DVD-RW restricted overwrite";
texts[0x14] = "DVD-RW sequential recording";
texts[0x15] = "DVD-R/DL sequential recording";
texts[0x16] = "DVD-R/DL layer jump recording";
texts[0x1a] = "DVD+RW";
texts[0x1b] = "DVD+R";
texts[0x2a] = "DVD+RW/DL";
texts[0x2b] = "DVD+R/DL";
texts[0x40] = "BD-ROM";
texts[0x41] = "BD-R sequential recording";
texts[0x42] = "BD-R random recording";
texts[0x43] = "BD-RE";
texts[0x50] = "HD-DVD-ROM";
texts[0x51] = "HD-DVD-R";
texts[0x52] = "HD-DVD-RAM";
}
if (profile_number<0 || profile_number>=max_pno)
return "";
return texts[profile_number];
}
/* ts A90603 : to be used if the drive knows no GET CONFIGURATION
*/
static int mmc_guess_profile(struct burn_drive *d, int flag)
{
int cp;
cp = 0;
if (d->status == BURN_DISC_BLANK ||
d->status == BURN_DISC_APPENDABLE) {
cp = 0x09;
} else if (d->status == BURN_DISC_FULL) {
cp = 0x08;
}
if (cp)
if (d->erasable)
cp = 0x0a;
d->current_profile = cp;
if (cp == 0)
return 0;
d->current_is_cd_profile = 1;
d->current_is_supported_profile = 1;
strcpy(d->current_profile_text, mmc_obtain_profile_name(cp));
return 1;
}
static int mmc_read_disc_info_al(struct burn_drive *d, int *alloc_len)
{
struct buffer *buf = NULL;
unsigned char *data;
struct command *c = NULL;
char *msg = NULL;
/* ts A70131 : had to move mmc_read_toc() to end of function */
int do_read_toc = 0, disc_status, len, old_alloc_len;
int ret, number_of_sessions = -1;
int key, asc, ascq;
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
/* ts A61020 */
d->start_lba = d->end_lba = -2000000000;
d->erasable = 0;
d->last_track_no = 1;
/* ts B10730 */
d->sent_default_page_05 = 0;
/* ts A70212 - A70215 */
d->media_capacity_remaining = 0;
d->media_lba_limit = 0;
/* ts A81210 */
d->media_read_capacity = 0x7fffffff;
d->mr_capacity_trusted = -1;
/* ts A61202 */
d->toc_entries = 0;
if (d->status == BURN_DISC_EMPTY)
{ret = 1; goto ex;}
mmc_get_configuration(d);
scsi_init_command(c, MMC_GET_DISC_INFO, sizeof(MMC_GET_DISC_INFO));
c->dxfer_len = *alloc_len;
c->opcode[7]= (c->dxfer_len >> 8) & 0xff;
c->opcode[8]= c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->sectors = 0;
c->page->bytes = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
if (c->error) {
spc_decode_sense(c->sense, 0, &key, &asc, &ascq);
if (key == 5 && asc == 0x20 && ascq == 0) {
/* ts B11031 : qemu -cdrom does not know
051h READ DISC INFORMATION
*/
ret = mmc_read_toc_fmt0(d);
if (ret > 0) {
/* >>> ??? anything more to be set ? */;
mmc_read_capacity(d);
*alloc_len = 0;
goto ex;
}
}
d->busy = BURN_DRIVE_IDLE;
{ret = 0; goto ex;}
}
data = c->page->data;
len = (data[0] << 8) | data[1];
old_alloc_len = *alloc_len;
*alloc_len = len + 2;
if (old_alloc_len < 34)
{ret = 1; goto ex;}
if (*alloc_len < 24) /* data[23] is the last mandatory byte here */
{ret = 0; goto ex;}
if (len + 2 > old_alloc_len)
len = old_alloc_len - 2;
d->erasable = !!(data[2] & 16);
/* ts A90908 */
d->disc_type = data[8];
d->disc_info_valid = 1;
d->disc_id = mmc_four_char_to_int(data + 12);
d->disc_info_valid |= (!!(data[7] & 128)) << 1;
if (len + 2 > 31 && (data[7] & 64)) {
memcpy(d->disc_bar_code, data + 24, 8);
d->disc_bar_code[8] = 0;
d->disc_info_valid |= 4;
}
if (len + 2 > 32 && (data[7] & 16)) {
d->disc_app_code = data[32];
d->disc_info_valid |= 8;
}
if (data[7] & 32)
d->disc_info_valid |= 16;
if (data[2] & 16)
d->disc_info_valid |= 32;
disc_status = data[2] & 3;
d->state_of_last_session = (data[2] >> 2) & 3;
number_of_sessions = (data[9] << 8) | data[4];
if (d->current_profile == 0x10 || d->current_profile == 0x40) {
/* DVD-ROM , BD-ROM */
disc_status = 2; /* always full and finalized */
d->erasable = 0; /* never erasable */
}
#ifdef Libburn_support_bd_r_readonlY
/* <<< For now: declaring BD-R read-only
*/
#ifndef Libburn_support_bd_plus_r_srM
if (d->current_profile == 0x41) {
/* BD-R seq as readonly dummy */
disc_status = 2; /* always full and finalized */
d->erasable = 0; /* never erasable */
}
#endif
if (d->current_profile == 0x42) {
/* BD-R rnd */
disc_status = 2; /* always full and finalized */
d->erasable = 0; /* never erasable */
}
#endif /* Libburn_support_bd_r_readonlY */
/* MMC-5 6.22.3.1.16:
Last Session Lead-in Start Address bytes 16 to 19
Last Possible Lead-out Start Address bytes 20 to 23
MSF for CD, LBA else
*/
if(d->current_profile == 0x08 || d->current_profile == 0x09 ||
d->current_profile == 0x0a) {
d->last_lead_in =
burn_msf_to_lba(data[17], data[18], data[19]);
d->last_lead_out =
burn_msf_to_lba(data[21], data[22], data[23]);
} else {
d->last_lead_in = mmc_four_char_to_int(data + 16);
d->last_lead_out = mmc_four_char_to_int(data + 20);
}
switch (disc_status) {
case 0:
regard_as_blank:;
d->toc_entries = 0;
/*
fprintf(stderr, "libburn_experimental: start_lba = %d (%d %d %d) , end_lba = %d (%d %d %d)\n",
d->start_lba, data[17], data[18], data[19],
d->end_lba, data[21], data[22], data[23]);
*/
d->status = BURN_DISC_BLANK;
d->start_lba = d->last_lead_in;
d->end_lba = d->last_lead_out;
break;
case 1:
case 2:
if (disc_status == 2)
d->status = BURN_DISC_FULL;
else
d->status = BURN_DISC_APPENDABLE;
/* ts A81210 */
ret = mmc_read_capacity(d);
/* Freshly formatted, unwritten BD-R pretend to be appendable
but in our model they need to be regarded as blank.
Criterion: BD-R seq, read capacity known and 0,
declared appendable, single empty session
*/
if (d->current_profile == 0x41 &&
d->status == BURN_DISC_APPENDABLE &&
ret > 0 && d->media_read_capacity == 0 &&
d->state_of_last_session == 0 && number_of_sessions == 1)
goto regard_as_blank;
if (d->current_profile == 0x41 &&
d->status == BURN_DISC_APPENDABLE &&
d->state_of_last_session == 1) {
/* ??? apply this test to other media types ? */
libdax_msgs_submit(libdax_messenger, d->global_index,
0x00020169,
LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH,
"Last session on media is still open.", 0, 0);
}
do_read_toc = 1;
break;
case 3:
/* ts A91009 : DVD-RAM has disc status "others" */
mmc_read_capacity(d);
break;
}
/* ts A90603 : An MMC-1 drive might not know the media type yet */
if (d->current_is_guessed_profile && d->current_profile == 0)
mmc_guess_profile(d, 0);
if ((d->current_profile != 0 || d->status != BURN_DISC_UNREADY)
&& ! d->current_is_supported_profile) {
if (!(d->silent_on_scsi_error == 1 ||
d->silent_on_scsi_error == 2)) {
msg = calloc(1, 160);
if (msg != NULL) {
sprintf(msg,
"Unsuitable media detected. Profile %4.4Xh %s",
d->current_profile, d->current_profile_text);
libdax_msgs_submit(libdax_messenger,
d->global_index, 0x0002011e,
d->silent_on_scsi_error == 3 ?
LIBDAX_MSGS_SEV_DEBUG :
LIBDAX_MSGS_SEV_SORRY,
LIBDAX_MSGS_PRIO_HIGH, msg, 0,0);
free(msg);
}
}
d->status = BURN_DISC_UNSUITABLE;
{ret = 0; goto ex;}
}
/* ts A61217 :
growisofs performs OPC if (data[0]<<8)|data[1]<=32
which indicates no OPC entries are attached to the
reply from the drive.
ts A91104 :
Actually growisofs performs OPC only on DVD-R[W].
*/
d->num_opc_tables = 0;
if(((data[0] << 8) | data[1]) > 32) /* i.e. > 34 bytes are available */
d->num_opc_tables = data[33];
/* ts A61219 : mmc5r03c.pdf 6.22.3.1.13 BG Format Status
0=blank (not yet started)
1=started but neither running nor complete
2=in progress
3=completed
*/
d->bg_format_status = data[7] & 3;
/* Preliminarily declare blank:
ts A61219 : DVD+RW (is not bg_format_status==0 "blank")
ts A61229 : same for DVD-RW Restricted overwrite
ts A70112 : same for DVD-RAM
*/
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
d->current_profile == 0x12 || d->current_profile == 0x43)
d->status = BURN_DISC_BLANK;
#ifdef Libburn_disc_with_incomplete_sessioN
/* ts B30112 : number of open sessions */
d->incomplete_sessions = 0;
#endif
if (d->status == BURN_DISC_BLANK) {
d->last_track_no = 1; /* The "incomplete track" */
d->complete_sessions = 0;
} else {
/* ts A70131 : number of closed sessions */
d->complete_sessions = number_of_sessions;
/* mmc5r03c.pdf 6.22.3.1.3 State of Last Session: 3=complete */
if (d->state_of_last_session != 3 &&
d->complete_sessions >= 1) {
d->complete_sessions--;
#ifdef Libburn_disc_with_incomplete_sessioN
d->incomplete_sessions++;
#endif
}
/* ts A70129 : mmc5r03c.pdf 6.22.3.1.7
This includes the "incomplete track" if the disk is
appendable. I.e number of complete tracks + 1. */
d->last_track_no = (data[11] << 8) | data[6];
}
if (d->current_profile != 0x0a && d->current_profile != 0x13 &&
d->current_profile != 0x14 && d->status != BURN_DISC_FULL)
d->erasable = 0; /* stay in sync with burn_disc_erase() */
if (do_read_toc)
mmc_read_toc(d);
ret = 1;
ex:
BURN_FREE_MEM(buf);
BURN_FREE_MEM(c);
return ret;
}
void mmc_read_disc_info(struct burn_drive *d)
{
int alloc_len = 34, ret;
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_disc_info") <= 0)
return;
ret = mmc_read_disc_info_al(d, &alloc_len);
/*
fprintf(stderr,"LIBBURN_DEBUG: 51h alloc_len = %d , ret = %d\n",
alloc_len, ret);
*/
if (ret <= 0)
return;
/* for now there is no need to inquire the variable length part */
}
/* @param flag bit= do not allocate text_packs
*/
static int mmc_get_leadin_text_al(struct burn_drive *d,
unsigned char **text_packs, int *alloc_len,
int flag)
{
struct buffer *buf = NULL;
struct command *c = NULL;
unsigned char *data;
int ret, data_length;
*text_packs = NULL;
BURN_ALLOC_MEM(buf, struct buffer, 1);
BURN_ALLOC_MEM(c, struct command, 1);
scsi_init_command(c, MMC_GET_LEADTEXT, sizeof(MMC_GET_LEADTEXT));
c->dxfer_len = *alloc_len;
c->opcode[7]= (c->dxfer_len >> 8) & 0xff;
c->opcode[8]= c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
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;
data_length = (data[0] << 8) + data[1];
*alloc_len = data_length + 2;
if (*alloc_len >= 22 && !(flag & 1)) {
BURN_ALLOC_MEM(*text_packs, unsigned char, *alloc_len - 4);
memcpy(*text_packs, data + 4, *alloc_len - 4);
}
ret = 1;
ex:;
BURN_FREE_MEM(c);
BURN_FREE_MEM(buf);
return ret;
}
/* ts B11201 */
/* Read the CD-TEXT data from the Lead-in of an Audio CD
*/
int mmc_get_leadin_text(struct burn_drive *d,
unsigned char **text_packs, int *num_packs, int flag)
{
int alloc_len = 4, ret;
*num_packs = 0;
if (mmc_function_spy(d, "mmc_get_leadin_text") <= 0)
return -1;
ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 1);
if (ret <= 0 || alloc_len < 22)
return (ret > 0 ? 0 : ret);
ret = mmc_get_leadin_text_al(d, text_packs, &alloc_len, 0);
if (ret <= 0 || alloc_len < 22) {
if (*text_packs != NULL)
free(*text_packs);
*text_packs = NULL;
return (ret > 0 ? 0 : ret);
}
*num_packs = (alloc_len - 4) / 18;
return ret;
}
void mmc_read_atip(struct burn_drive *d)
{
struct buffer *buf = NULL;
struct command *c = NULL;
int alloc_len = 28;
/* ts A61021 */
unsigned char *data;
/* Speed values from A1:
With 4 cdrecord tells "10" or "8" where MMC-1 says "8".
cdrecord "8" appear on 4xCD-RW and thus seem to be quite invalid.
My CD-R (>=24 speed) tell no A1.
The higher non-MMC-1 values are hearsay.
*/
/* 0, 2, 4, 6, 10, -, 16, -, */
static int speed_value[16]= { 0, 353, 706, 1059, 1764, -5, 2824, -7,
4234, 5646, 7056, 8468, -12, -13, -14, -15};
/* 24, 32, 40, 48, -, -, -, - */
BURN_ALLOC_MEM_VOID(buf, struct buffer, 1);
BURN_ALLOC_MEM_VOID(c, struct command, 1);
mmc_start_if_needed(d, 1);
if (mmc_function_spy(d, "mmc_read_atip") <= 0)
goto ex;
scsi_init_command(c, MMC_GET_ATIP, sizeof(MMC_GET_ATIP));
c->dxfer_len = alloc_len;
c->opcode[7] = (c->dxfer_len >> 8) & 0xff;
c->opcode[8] = c->dxfer_len & 0xff;
c->retry = 1;
c->page = buf;
c->page->bytes = 0;
c->page->sectors = 0;
c->dir = FROM_DRIVE;
d->issue_command(d, c);
/* ts B00501 : now caring for error */
if (c->error) {
d->erasable = 0;
d->start_lba = 0;
d->end_lba = 0;
goto ex;
}
/* ts A61021 */
data = c->page->data;
d->erasable = !!(data[6]&64);
d->start_lba = burn_msf_to_lba(data[8],data[9],data[10]);
d->end_lba = burn_msf_to_lba(data[12],data[13],data[14]);
/* ts B21124 : LITE-ON LTR-48125S returns crap on pressed
audio CD and CD-ROM
*/
if (d->start_lba >= d->end_lba) {
d->start_lba = 0;
d->end_lba = 0;
}
if (data[6]&4) {
if (speed_value[(data[16]>>4)&7] > 0) {
d->mdata->min_write_speed =
speed_value[(data[16]>>4)&7];
if (speed_value[(data[16])&15] <= 0)
d->mdata->max_write_speed =
speed_value[(data[16]>>4)&7];
}
if (speed_value[(data[16])&15] > 0) {
d->mdata->max_write_speed =
speed_value[(data[16])&15];
if (speed_value[(data[16]>>4)&7] <= 0)
d->mdata->min_write_speed =
speed_value[(data[16])&15];
}
}
#ifdef Burn_mmc_be_verbous_about_atiP
{ int i;
fprintf(stderr,"libburn_experimental: Returned ATIP Data\n");
for(i= 0; i<28; i++)
fprintf(stderr,"%3.3d (0x%2.2x)%s",
data[i],data[i],(((i + 1) % 5) ? " " : "\n"));
fprintf(stderr,"\n");
fprintf(stderr,
"libburn_experimental: Indicative Target Writing Power= %d\n",
(data[4]>>4)&7);
fprintf(stderr,
"libburn_experimental: Reference speed= %d ->%d\n",
data[4]&7, speed_value[data[4]&7]);
fprintf(stderr,
"libburn_experimental: Is %sunrestricted\n",
(data[5]&64?"":"not "));
fprintf(stderr,
"libburn_experimental: Is %serasable, sub-type %d\n",
(data[6]&64?"":"not "),(data[6]>>3)&3);
fprintf(stderr,
"libburn_experimental: lead in: %d (%-2.2d:%-2.2d/%-2.2d)\n",
burn_msf_to_lba(data[8],data[9],data[10]),
data[8],data[9],data[10]);
fprintf(stderr,
"libburn_experimental: lead out: %d (%-2.2d:%-2.2d/%-2.2d)\n",
burn_msf_to_lba(data[12],data[13],data[14]),
data[12],data[13],data[14]);
if(data[6]&4)
fprintf(stderr,
"libburn_experimental: A1 speed low %d speed high %d\n",
speed_value[(data[16]>>4)&7], speed_value[(data[16])&7]);
if(data[6]&2)
fprintf(stderr,
"libburn_experimental: A2 speed low %d speed high %d\n",
speed_value[(data[20]>>4)&7], speed_value[(data[20])&7]);
if(data[6]&1)
fprintf(stderr,
"libburn_experimental: A3 speed low %d speed high %d\n",
speed_value[(data[24]>>4)&7], speed_value[(data[24])&7]);
}