You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5388 lines
150 KiB
C
5388 lines
150 KiB
C
/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */
|
|
|
|
/* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens
|
|
Copyright (c) 2006 - 2015 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 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
|
|
|
|
/* 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 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. :))
|
|
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, "Upcomming track: ");
|
|
if (d->current_profile == 0x1a || d->current_profile == 0x13 ||
|
|
d->current_profile == 0x12 || d->current_profile == 0x43) {
|
|
/* overwriteable */
|
|
*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;
|
|
|
|
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;
|
|
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;
|
|
}
|
|
if (spc_wait_unit_attention(d, 3600, "CLOSE TRACK SESSION", 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;
|
|
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 < (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;
|
|
struct timezone tz;
|
|
int ret, diff;
|
|
|
|
ret = gettimeofday(&now, &tz);
|
|
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, 160);
|
|
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);
|
|
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) {
|
|
|
|
/* >>> ??? is WRITE12 available ? */
|
|
/* >>> ??? inquire feature 107h Stream Writing bit ? */
|
|
|
|
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, 256);
|
|
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);
|
|
free(msg);
|
|
}
|
|
d->cancel = 1;
|
|
|
|
return BE_CANCELLED;
|
|
}
|
|
|
|
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;
|
|
/* Prefering 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_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;
|
|
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 wether 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;
|
|
/* Prefering 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 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);
|
|
|
|
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 eventually loaded TOC before issueing
|
|
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;
|
|
|
|
/* 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:
|
|
d->status = BURN_DISC_APPENDABLE;
|
|
|
|
case 2:
|
|
if (disc_status == 2)
|
|
d->status = BURN_DISC_FULL;
|
|
|
|
/* 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]);
|
|
}
|
|
|
|
#endif /* Burn_mmc_be_verbous_about_atiP */
|
|
|
|
/* ts A61020
|
|
http://www.t10.org/ftp/t10/drafts/mmc/mmc-r10a.pdf , table 77 :
|
|
|
|
0 ATIP Data Length MSB
|
|
1 ATIP Data Length LSB
|
|
2 Reserved
|
|
3 Reserved
|
|
4 bit7=1, bit4-6="Indicative Target Writing Power", bit3=reserved ,
|
|
bit0-2="Reference speed"
|
|
5 bit7=0, bit6="URU" , bit0-5=reserved
|
|
6 bit7=1, bit6="Disc Type", bit3-4="Disc Sub-Type",
|
|
bit2="A1", bit1="A2", bit0="A3"
|
|
7 reserved
|
|
8 ATIP Start Time of lead-in (Min)
|
|
9 ATIP Start Time of lead-in (Sec)
|
|
10 ATIP Start Time of lead-in (Frame)
|
|
11 reserved
|
|
12 ATIP Last Possible Start Time of lead-out (Min)
|
|
13 ATIP Last Possible Start Time of lead-out (Sec)
|
|
14 ATIP Last Possible Start Time of lead-out (Frame)
|
|
15 reserved
|
|
16 bit7=0, bit4-6="Lowest Usable CLV Recording speed"
|
|
bit0-3="Highest Usable CLV Recording speed"
|
|
17 bit7=0, bit4-6="Power Multiplication Factor p",
|
|
bit1-3="Target y value of the Modulation/Power function", bit0=reserved
|
|
18 bit7=1, bit4-6="Recommended Erase/Write Power Ratio (P(inf)/W(inf))"
|
|
bit0-3=reserved
|
|
19 reserved
|
|
20-22 A2 Values
|
|
23 reserved
|
|
24-26 A3 Values
|
|
27 reserved
|
|
|
|
Disc Type - zero indicates CD-R media; one indicates CD-RW media.
|
|
|
|
Disc Sub-Type - shall be set to zero.
|
|
|
|
A1 - when set to one, indicates that bytes 16-18 are valid.
|
|
|
|
Lowest Usable CLV Recording Speed
|
|
000b Reserved
|
|
001b 2X
|
|
010b - 111b Reserved
|
|
|
|
Highest CLV Recording Speeds
|
|
000b Reserved
|
|
001b 2X
|
|
010b 4X
|
|
011b 6X
|
|
100b 8X
|
|
101b - 111b Reserved
|
|
|
|
MMC-3 seems to recommend MODE SENSE (5Ah) page 2Ah rather than A1, A2, A3.
|
|
This page is loaded in libburn function spc_sense_caps() .
|
|
Speed is given in kbytes/sec there. But i suspect this to be independent
|
|
of media. So one would habe to associate the speed descriptor blocks with
|
|
the ATIP media characteristics ? How ?
|
|
|
|
*/
|
|
|
|
ex:;
|
|
BURN_FREE_MEM(buf);
|
|
BURN_FREE_MEM(c);
|
|
}
|
|
|
|
|
|
int mmc_eval_read_error(struct burn_drive *d, struct command *c, char *what,
|
|
int start_m, int start_s, int start_f,
|
|
int end_m, int end_s, int end_f, int flag)
|
|
{
|
|
char *msg = NULL;
|
|
int key, asc, ascq, silent;
|
|
|
|
if (!c->error)
|
|
return 0;
|
|
|
|
msg = calloc(1, 256);
|
|
if (msg != NULL) {
|
|
if (start_s < 0 || start_f < 0 || end_s < 0 || end_f < 0) {
|
|
sprintf(msg,
|
|
"SCSI error on %s(%d,%d): ", what, start_m, end_m);
|
|
} else {
|
|
sprintf(msg, "SCSI error on %s(%dm%ds%df,%dm%ds%df): ",
|
|
what,
|
|
start_m, start_s, start_f, end_m, end_s, end_f);
|
|
}
|
|
scsi_error_msg(d, c->sense, 14, msg + strlen(msg),
|
|
&key, &asc, &ascq);
|
|
silent = (d->silent_on_scsi_error == 1);
|
|
if (key == 5 && asc == 0x64 && ascq == 0x0) {
|
|
d->had_particular_error |= 1;
|
|
if (d->silent_on_scsi_error == 2)
|
|
silent = 1;
|
|
}
|
|
if(!silent)
|
|
libdax_msgs_submit(libdax_messenger,
|
|
d->global_index,
|
|
0x00020144,
|
|
d->silent_on_scsi_error == 3 ?
|
|
LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY,
|
|
LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0);
|
|
free(msg);
|
|
}
|
|
return BE_CANCELLED;
|
|
}
|
|
|
|
|
|
/* ts B21119 : Derived from older mmc_read_sectors()
|
|
@param flag bit0= set DAP bit (also with o->dap_bit)
|
|
*/
|
|
int mmc_read_cd_msf(struct burn_drive *d,
|
|
int start_m, int start_s, int start_f,
|
|
int end_m, int end_s, int end_f,
|
|
int sec_type, int main_ch,
|
|
const struct burn_read_opts *o, struct buffer *buf, int flag)
|
|
{
|
|
int req, ret, dap_bit;
|
|
int subcodes_audio = 0, subcodes_data = 0;
|
|
struct command *c;
|
|
#ifdef Libburn_mmc_report_recovereD
|
|
int report_recovered_errors = 0;
|
|
#endif
|
|
|
|
c = &(d->casual_command);
|
|
mmc_start_if_needed(d, 0);
|
|
if (mmc_function_spy(d, "mmc_read_cd_msf") <= 0)
|
|
return -1;
|
|
|
|
dap_bit = flag & 1;
|
|
if (o != NULL) {
|
|
subcodes_audio = o->subcodes_audio;
|
|
subcodes_data = o->subcodes_data;
|
|
dap_bit |= o->dap_bit;
|
|
|
|
#ifdef Libburn_mmc_report_recovereD
|
|
report_recovered_errors = o->report_recovered_errors;
|
|
#endif
|
|
}
|
|
|
|
scsi_init_command(c, MMC_READ_CD_MSF, sizeof(MMC_READ_CD_MSF));
|
|
c->retry = 1;
|
|
c->opcode[1] = ((sec_type & 7) << 2) | ((!!dap_bit) << 1);
|
|
c->opcode[3] = start_m;
|
|
c->opcode[4] = start_s;
|
|
c->opcode[5] = start_f;
|
|
c->opcode[6] = end_m;
|
|
c->opcode[7] = end_s;
|
|
c->opcode[8] = end_f;
|
|
|
|
req = main_ch & 0xf8;
|
|
|
|
#ifdef Libburn_mmc_report_recovereD
|
|
/* ts A61106 : LG GSA-4082B dislikes this. key=5h asc=24h ascq=00h */
|
|
if (d->busy == BURN_DRIVE_GRABBING || report_recovered_errors)
|
|
req |= 2;
|
|
#endif /* Libburn_mmc_report_recovereD */
|
|
|
|
c->opcode[9] = req;
|
|
|
|
c->opcode[10] = 0;
|
|
/* always read the subcode, throw it away later, since we don't know
|
|
what we're really reading
|
|
*/
|
|
/* >>> ts B21125 : This is very obscure:
|
|
MMC-3 has sub channel selection 001b as "RAW"
|
|
MMC-5 does neither mention 001b nor "RAW".
|
|
And why should a non-grabbed drive get here ?
|
|
*/
|
|
if (d->busy == BURN_DRIVE_GRABBING || subcodes_audio || subcodes_data)
|
|
c->opcode[10] = 1;
|
|
|
|
/* <<< ts B21125 : test with sub channel selection 100b
|
|
no data, only sub channel
|
|
c->opcode[9] = 0;
|
|
c->opcode[10] = 4;
|
|
Did not help either with reading before LBA -150
|
|
*/
|
|
/* <<< ts B21125 : test with sub channel selection 001b and no user data
|
|
c->opcode[9] = 0;
|
|
c->opcode[10] = 1;
|
|
*/
|
|
|
|
c->page = buf;
|
|
c->dir = FROM_DRIVE;
|
|
d->issue_command(d, c);
|
|
ret = mmc_eval_read_error(d, c, "read_cd_msf",
|
|
start_m, start_s, start_f,
|
|
end_m, end_s, end_f, 0);
|
|
return ret;
|
|
}
|
|
|
|
|
|
/* ts B21119 : Derived from older mmc_read_sectors()
|
|
@param flag bit0= set DAP bit (also with o->dap_bit)
|
|
*/
|
|
int mmc_read_cd(struct burn_drive < |