libburn/libburn/mmc.c

2957 lines
83 KiB
C
Raw Normal View History

2006-08-15 20:37:04 +00:00
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
/* ts A61009 */
/* #include <a ssert.h> */
2006-08-15 20:37:04 +00:00
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
2006-08-15 20:37:04 +00:00
#include <pthread.h>
#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"
/* ts A70223 : in init.c */
extern int burn_support_untested_profiles;
#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 knowlege 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
2007-01-12 16:26:31 +00:00
/* ts A70112 */
#define Libburn_support_dvd_raM 1
/* ts A70129 */
#define Libburn_support_dvd_r_seQ 1
2007-04-18 10:36:35 +00:00
/* ts A70306 */
#define Libburn_support_dvd_plus_R 1
2007-04-18 10:36:35 +00:00
/* DVD 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 wether 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. :))
2007-01-01 17:10:54 +00:00
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.
2007-01-12 16:26:31 +00:00
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.
2007-02-05 13:28:57 +00:00
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.
2007-02-10 17:23:49 +00:00
ts A70208 : Finally made tests with DVD-R. Worked exactly as new DVD-RW.
2007-04-18 10:36:35 +00:00
ts A70306 : Implemented DVD+R (always -multi for now)
ts A70330 : Allowed finalizing of DVD+R.
*/
/* 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
static unsigned char MMC_GET_MSINFO[] =
{ 0x43, 0, 1, 0, 0, 0, 0, 16, 0, 0 };
2006-08-15 20:37:04 +00:00
static unsigned char MMC_GET_TOC[] = { 0x43, 2, 2, 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_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 };
2006-08-15 20:37:04 +00:00
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, */
2006-08-15 20:37:04 +00:00
static unsigned char MMC_GET_CONFIGURATION[] =
{ 0x46, 0, 0, 0, 0, 0, 0, 16, 0, 0 };
2006-08-15 20:37:04 +00:00
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, 16, 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 };
2006-08-15 20:37:04 +00:00
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};
2007-01-09 21:06:55 +00:00
/* 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};
2007-02-05 13:28:57 +00:00
/* 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};
2007-08-12 15:25:56 +00:00
/* 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};
2007-01-09 21:06:55 +00:00
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;
}
2006-08-15 20:37:04 +00:00
void mmc_send_cue_sheet(struct burn_drive *d, struct cue_sheet *s)
{
struct buffer buf;
struct command c;
if (mmc_function_spy(d, "mmc_send_cue_sheet") <= 0)
return;
scsi_init_command(&c, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET));
/*
2006-08-15 20:37:04 +00:00
c.oplen = sizeof(MMC_SEND_CUE_SHEET);
memcpy(c.opcode, MMC_SEND_CUE_SHEET, sizeof(MMC_SEND_CUE_SHEET));
*/
c.retry = 1;
2006-08-15 20:37:04 +00:00
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);
}
2007-02-05 13:28:57 +00:00
/* 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];
2007-02-05 13:28:57 +00:00
if (mmc_function_spy(d, "mmc_reserve_track") <= 0)
return 0;
scsi_init_command(&c, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK));
/*
2007-02-05 13:28:57 +00:00
c.oplen = sizeof(MMC_RESERVE_TRACK);
memcpy(c.opcode, MMC_RESERVE_TRACK, sizeof(MMC_RESERVE_TRACK));
*/
c.retry = 1;
/* Round to 32 KiB and divide by 2048
(by nice binary rounding trick learned from dvd+rw-tools) */
2007-02-05 13:28:57 +00:00
lba = ((size + (off_t) 0x7fff) >> 11) & ~0xf;
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);
2007-02-05 13:28:57 +00:00
c.page = NULL;
c.dir = NO_TRANSFER;
d->issue_command(d, &c);
return !c.error;
2007-02-05 13:28:57 +00:00
}
/* 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;
if (mmc_function_spy(d, "mmc_read_track_info") <= 0)
return 0;
scsi_init_command(&c, MMC_TRACK_INFO, sizeof(MMC_TRACK_INFO));
/*
c.oplen = sizeof(MMC_TRACK_INFO);
memcpy(c.opcode, 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 )
/* DVD+RW , DVD-RW restricted overwrite , DVD-RAM */
trackno = 1;
else if (d->current_profile == 0x10 ||
d->current_profile == 0x11 ||
d->current_profile == 0x14 ||
d->current_profile == 0x15)
/* DVD-ROM , DVD-R[W] Sequential */
trackno = d->last_track_no;
else /* mmc5r03c.pdf: valid only for CD, DVD+R, DVD+R DL */
trackno = 0xFF;
}
2007-03-01 12:07:29 +00:00
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)
2006-08-15 20:37:04 +00:00
{
struct buffer buf;
int ret, num, alloc_len = 20;
unsigned char *data;
2006-08-15 20:37:04 +00:00
if (mmc_function_spy(d, "mmc_get_nwa") <= 0)
return -1;
ret = mmc_read_track_info(d, trackno, &buf, alloc_len);
if (ret <= 0)
return ret;
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);
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
d->current_profile == 0x12) {
/* overwriteable */
*lba = *nwa = num = 0;
} else if (!(data[7]&1)) {
/* ts A61106 : MMC-1 Table 142 : NWA_V = NWA Valid Flag */
libdax_msgs_submit(libdax_messenger, -1, 0x00000002,
LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO,
"mmc_get_nwa: Track Info Block: NWA_V == 0", 0, 0);
return 0;
}
if (num > 0) {
d->media_capacity_remaining = ((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);
*/
return 1;
2006-08-15 20:37:04 +00:00
}
/* 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)
2006-08-15 20:37:04 +00:00
{
struct burn_drive *d;
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); */
d = o->drive;
2006-08-15 20:37:04 +00:00
o->multi = 0;
spc_select_write_params(d, 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)
2006-08-15 20:37:04 +00:00
{
struct burn_drive *d;
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); */
d = o->drive;
2006-08-15 20:37:04 +00:00
o->multi = 3;
spc_select_write_params(d, o);
mmc_close(d, 1, 0);
}
2007-03-01 12:07:29 +00:00
/* 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
*/
2006-08-15 20:37:04 +00:00
void mmc_close(struct burn_drive *d, int session, int track)
{
struct command c;
if (mmc_function_spy(d, "mmc_close") <= 0)
return;
scsi_init_command(&c, MMC_CLOSE, sizeof(MMC_CLOSE));
/*
2006-08-15 20:37:04 +00:00
c.oplen = sizeof(MMC_CLOSE);
memcpy(c.opcode, MMC_CLOSE, sizeof(MMC_CLOSE));
*/
c.retry = 1;
2007-03-01 12:07:29 +00:00
/* (ts A61030 : shifted !!session rather than or-ing plain session ) */
c.opcode[2] = ((session & 3) << 1) | !!track;
2006-08-15 20:37:04 +00:00
c.opcode[4] = track >> 8;
c.opcode[5] = track & 0xFF;
c.page = NULL;
c.dir = NO_TRANSFER;
d->issue_command(d, &c);
}
void mmc_get_event(struct burn_drive *d)
{
struct buffer buf;
struct command c;
int alloc_len= 8;
2006-08-15 20:37:04 +00:00
if (mmc_function_spy(d, "mmc_get_event") <= 0)
return;
scsi_init_command(&c, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
/*
2006-08-15 20:37:04 +00:00
c.oplen = sizeof(MMC_GET_EVENT);
memcpy(c.opcode, MMC_GET_EVENT, sizeof(MMC_GET_EVENT));
*/
c.dxfer_len = alloc_len;
c.opcode[7] = (c.dxfer_len >> 8) & 0xff;
c.opcode[8] = c.dxfer_len & 0xff;
c.retry = 1;
2006-08-15 20:37:04 +00:00
c.page = &buf;
c.page->bytes = 0;
c.page->sectors = 0;
c.dir = FROM_DRIVE;
d->issue_command(d, &c);
burn_print(12, "0x%x:0x%x:0x%x:0x%x\n",
c.page->data[0], c.page->data[1], c.page->data[2],
c.page->data[3]);
burn_print(12, "event: %d:%d:%d:%d\n", c.page->data[4],
c.page->data[5], c.page->data[6], c.page->data[7]);
}
/* 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;
struct timezone dummy_tz;
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,&dummy_tz);
#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,&dummy_tz);
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 < d->wfb_min_usec)
usec = d->wfb_min_usec;
else if (usec > 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;
}
2006-08-15 20:37:04 +00:00
void mmc_write_12(struct burn_drive *d, int start, struct buffer *buf)
{
struct command c;
int len;
if (mmc_function_spy(d, "mmc_write_12") <= 0)
return;
2006-08-15 20:37:04 +00:00
len = buf->sectors;
/* ts A61009 */
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
2006-08-15 20:37:04 +00:00
burn_print(100, "trying to write %d at %d\n", len, start);
scsi_init_command(&c, MMC_WRITE_12, sizeof(MMC_WRITE_12));
/*
2006-08-15 20:37:04 +00:00
memcpy(c.opcode, MMC_WRITE_12, sizeof(MMC_WRITE_12));
c.oplen = sizeof(MMC_WRITE_12);
*/
c.retry = 1;
2007-03-01 12:07:29 +00:00
mmc_int_to_four_char(c.opcode + 2, start);
mmc_int_to_four_char(c.opcode + 6, len);
2006-08-15 20:37:04 +00:00
c.page = buf;
c.dir = TO_DRIVE;
d->issue_command(d, &c);
/* ts A70711 */
d->pessimistic_buffer_free -= buf->bytes;
d->pbf_altered = 1;
2006-08-15 20:37:04 +00:00
}
int mmc_write(struct burn_drive *d, int start, struct buffer *buf)
{
int cancelled;
struct command c;
int len;
#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,S_IRUSR|S_IWUSR);
#endif /* Libburn_log_in_and_out_streaM */
if (mmc_function_spy(d, "mmc_write") <= 0)
return BE_CANCELLED;
2006-08-15 20:37:04 +00:00
cancelled = d->cancel;
2006-08-15 20:37:04 +00:00
if (cancelled)
return BE_CANCELLED;
/* ts A70215 */
if (d->media_lba_limit > 0 && start >= d->media_lba_limit) {
char msg[160];
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);
d->cancel = 1; /* No need for mutexing because atomic */
return BE_CANCELLED;
}
2006-08-15 20:37:04 +00:00
len = buf->sectors;
/* ts A61009 : buffer fill problems are to be handled by caller */
/* a ssert(buf->bytes >= buf->sectors);*/ /* can be == at 0... */
2006-08-15 20:37:04 +00:00
burn_print(100, "trying to write %d at %d\n", len, start);
/* ts A70711 */
if(d->wait_for_buffer_free)
mmc_wait_for_buffer_free(d, buf);
scsi_init_command(&c, MMC_WRITE_10, sizeof(MMC_WRITE_10));
/*
2006-08-15 20:37:04 +00:00
memcpy(c.opcode, MMC_WRITE_10, sizeof(MMC_WRITE_10));
c.oplen = sizeof(MMC_WRITE_10);
*/
c.retry = 1;
2007-03-01 12:07:29 +00:00
mmc_int_to_four_char(c.opcode + 2, start);
2006-08-15 20:37:04 +00:00
c.opcode[6] = 0;
c.opcode[7] = (len >> 8) & 0xFF;
c.opcode[8] = len & 0xFF;
c.page = buf;
c.dir = TO_DRIVE;
/*
burn_print(12, "%d, %d, %d, %d - ", c->opcode[2], c->opcode[3], c->opcode[4], c->opcode[5]);
burn_print(12, "%d, %d, %d, %d\n", c->opcode[6], c->opcode[7], c->opcode[8], c->opcode[9]);
*/
#ifdef Libburn_log_in_and_out_streaM
/* <<< ts A61031 */
if(tee_fd!=-1) {
write(tee_fd,c.page->data,len*2048);
}
#endif /* Libburn_log_in_and_out_streaM */
2006-08-15 20:37:04 +00:00
d->issue_command(d, &c);
/* ts A70711 */
d->pessimistic_buffer_free -= buf->bytes;
d->pbf_altered = 1;
/* ts A61112 : react on eventual error condition */
if (c.error && c.sense[2]!=0) {
/* >>> make this scsi_notify_error() when liberated */
if (c.sense[2]!=0) {
char msg[160];
sprintf(msg,
"SCSI error on write(%d,%d): key=%X asc=%2.2Xh ascq=%2.2Xh",
start, len,
c.sense[2],c.sense[12],c.sense[13]);
libdax_msgs_submit(libdax_messenger, d->global_index,
0x0002011d,
LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,
msg, 0, 0);
}
d->cancel = 1;
return BE_CANCELLED;
}
2006-08-15 20:37:04 +00:00
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)
{
int min, sec, frames, num;
/* mark DVD extensions as valid */
entry->extensions_valid |= 1;
/* 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;
return 1;
}
/* 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;
int i, session_number, prev_session = -1, ret, lba, alloc_len = 34;
unsigned char *tdata, size_data[4], start_data[4];
char msg[160];
if (mmc_function_spy(d, "mmc_fake_toc") <= 0)
return -1;
if (d->last_track_no <= 0 || d->complete_sessions <= 0 ||
d->status == BURN_DISC_BLANK)
return 2;
if (d->last_track_no > BURN_MMC_FAKE_TOC_MAX_SIZE) {
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);
return 0;
}
d->disc = burn_disc_create();
if (d->disc == NULL)
return -1;
d->toc_entries = d->last_track_no + d->complete_sessions;
d->toc_entry = malloc(d->toc_entries * sizeof(struct burn_toc_entry));
if (d->toc_entry == NULL)
return -1;