diff --git a/cdrskin/cdrskin_timestamp.h b/cdrskin/cdrskin_timestamp.h index 5ef7d28..dff8843 100644 --- a/cdrskin/cdrskin_timestamp.h +++ b/cdrskin/cdrskin_timestamp.h @@ -1 +1 @@ -#define Cdrskin_timestamP "2006.12.18.123242" +#define Cdrskin_timestamP "2006.12.20.111932" diff --git a/libburn/libdax_msgs.h b/libburn/libdax_msgs.h index d7e35bf..ed81abc 100644 --- a/libburn/libdax_msgs.h +++ b/libburn/libdax_msgs.h @@ -334,8 +334,12 @@ Range "scdbackup" : 0x00020000 to 0x0002ffff 0x0002011a (NOTE,HIGH) = Padding up track to minimum size 0x0002011b (FATAL,HIGH) = Attempt to read track info from ungrabbed drive 0x0002011c (FATAL,HIGH) = Attempt to read track info from busy drive - 0x0002011d (FATAL,HIGH) = SCSI error condition on write - 0x0002011e (SORRY, HIGH) = Unsuitable media detected + 0x0002011d (FATAL,HIGH) = SCSI error on write + 0x0002011e (SORRY,HIGH) = Unsuitable media detected + 0x0002011f (SORRY,HIGH) = Burning of DVD+RW is restricted to a single track + 0x00020120 (NOTE,HIGH) = FORMAT UNIT ignored + 0x00020121 (FATAL,HIGH) = Write preparation setup failed + 0x00020122 (FATAL,HIGH) = SCSI error on format_unit libdax_audioxtr: 0x00020200 (SORRY,HIGH) = Cannot open audio source file diff --git a/libburn/mmc.c b/libburn/mmc.c index 88bada8..0228b55 100644 --- a/libburn/mmc.c +++ b/libburn/mmc.c @@ -34,6 +34,23 @@ extern struct libdax_msgs *libdax_messenger; +/* ts A61219 : ! HIGHLY EXPERIMENTAL ! + Based on knowlege from dvd+rw-tools-7.0 and mmc5r03c.pdf +*/ +/* +#define Libburn_support_dvd_plus_rW 1 +*/ +/* Progress report (with Libburn_support_dvd_plus_rW defined): + 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(). I did not exceed the formatted range + as reported by dvd+rw-mediainfo. +*/ + 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 }; @@ -62,6 +79,9 @@ static unsigned char MMC_SEND_CUE_SHEET[] = /* 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 }; + static int mmc_function_spy_do_tell = 0; @@ -310,9 +330,10 @@ int mmc_write(struct burn_drive *d, int start, struct buffer *buf) /* >>> make this scsi_notify_error() when liberated */ if (c.sense[2]!=0) { - char msg[80]; + char msg[160]; sprintf(msg, - "SCSI error condition on write : key=%X asc=%2.2Xh ascq=%2.2Xh", + "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, @@ -355,7 +376,7 @@ void mmc_read_toc(struct burn_drive *d) /* 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. - DVD+RW burns go on after passing through here. + outdated info: DVD+RW burns go on after passing through here. d->busy = BURN_DRIVE_IDLE; */ @@ -479,7 +500,7 @@ void mmc_read_disc_info(struct burn_drive *d) mmc_get_configuration(d); if ((d->current_profile != 0 || d->status != BURN_DISC_UNREADY) - && ! d->current_is_cd_profile) { + && ! d->current_is_supported_profile) { if (!d->silent_on_scsi_error) { sprintf(msg, "Unsuitable media detected. Profile %4.4Xh %s", @@ -536,12 +557,24 @@ void mmc_read_disc_info(struct burn_drive *d) break; } - /* ts A61217 : Note for future - >>> growisofs performs OPC if (data[0]<<8)|data[1]<=32 - >>> which indicates no OPC entries are attached to the - >>> reply from the drive. + /* >>> ts A61217 : Note for future + growisofs performs OPC if (data[0]<<8)|data[1]<=32 + which indicates no OPC entries are attached to the + reply from the drive. */ + /* 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; + + /* <<< ts A61219 : preliminarily declare all DVD+RW blank, + (which is not the same as bg_format_status==0 "blank") */ + if (d->current_profile == 0x1a) + d->status = BURN_DISC_BLANK; } void mmc_read_atip(struct burn_drive *d) @@ -893,6 +926,7 @@ void mmc_get_configuration(struct burn_drive *d) d->current_profile = 0; d->current_profile_text[0] = 0; d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; mmc_function_spy("mmc_get_configuration"); memcpy(c.opcode, MMC_GET_CONFIGURATION, sizeof(MMC_GET_CONFIGURATION)); @@ -917,7 +951,13 @@ void mmc_get_configuration(struct burn_drive *d) d->current_profile = cp; strcpy(d->current_profile_text, mmc_obtain_profile_name(cp)); if (cp == 0x08 || cp == 0x09 || cp == 0x0a) - d->current_is_cd_profile = 1; + d->current_is_supported_profile = d->current_is_cd_profile = 1; + +#ifdef Libburn_support_dvd_plus_rW + if (cp == 0x1a) + d->current_is_supported_profile = 1; +#endif + } void mmc_sync_cache(struct burn_drive *d) @@ -975,6 +1015,77 @@ int mmc_read_buffer_capacity(struct burn_drive *d) } +/* ts A61219 : learned much from dvd+rw-tools-7.0: plus_rw_format() */ +int mmc_format_unit(struct burn_drive *d) +{ + struct buffer buf; + struct command c; + int ret; + char msg[160],descr[80]; + + mmc_function_spy("mmc_format_unit"); + c.retry = 1; + c.oplen = sizeof(MMC_FORMAT_UNIT); + memcpy(c.opcode, MMC_FORMAT_UNIT, sizeof(MMC_FORMAT_UNIT)); + c.page = &buf; + c.page->bytes = 12; + c.page->sectors = 0; + c.dir = TO_DRIVE; + memset(c.page->data, 0, c.page->bytes); + + descr[0] = 0; + if (d->current_profile == 0x1a) { /* DVD+RW */ + c.page->data[1] = 0x02; /* Immed */ + c.page->data[3] = 8; /* Format descriptor length */ + /* mmc5r03c.pdf , 6.5.4.2.14 */ + c.page->data[8] = 0x26 << 2; /* Format type */ + memset(c.page->data + 4, 0xff, 4); /* maximum blocksize */ + if (d->bg_format_status == 1) /* is partly formatted */ + c.page->data[11] = 1; /* Restart bit */ + else if(d->bg_format_status == 2) { /* format in progress */ + strcpy(msg,"FORMAT UNIT ignored. Already in progress"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020120, + LIBDAX_MSGS_SEV_NOTE, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + } + sprintf(descr, "DVD+RW, BGFS %d", + d->bg_format_status); + } else { + + /* >>> other formattable types to come */ + + 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, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + + d->issue_command(d, &c); + if (c.error) { + if (c.sense[2]!=0) { + sprintf(msg, + "SCSI error on format_unit(%s): key=%X asc=%2.2Xh ascq=%2.2Xh", + descr, + c.sense[2],c.sense[12],c.sense[13]); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020122, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0, 0); + } + return 0; + } + + for (ret = 0; ret <= 0 ;) + ret = spc_test_unit_ready(d); + mmc_sync_cache(d); + return 1; +} + + /* ts A61021 : the mmc specific part of sg.c:enumerate_common() */ int mmc_setup_drive(struct burn_drive *d) @@ -993,6 +1104,7 @@ int mmc_setup_drive(struct burn_drive *d) d->close_session = mmc_close_session; d->close_track_session = mmc_close; d->read_buffer_capacity = mmc_read_buffer_capacity; + d->format_unit = mmc_format_unit; /* ts A61020 */ d->start_lba = -2000000000; @@ -1003,6 +1115,8 @@ int mmc_setup_drive(struct burn_drive *d) d->current_profile = -1; d->current_profile_text[0] = 0; d->current_is_cd_profile = 0; + d->current_is_supported_profile = 0; + d->bg_format_status = -1; return 1; } diff --git a/libburn/options.c b/libburn/options.c index c4c01b3..941d589 100644 --- a/libburn/options.c +++ b/libburn/options.c @@ -32,6 +32,7 @@ struct burn_write_opts *burn_write_opts_new(struct burn_drive *drive) opts->simulate = 0; opts->underrun_proof = drive->mdata->underrun_proof; opts->perform_opc = 1; + opts->obs = -1; opts->has_mediacatalog = 0; opts->format = BURN_CDROM; opts->multi = 0; diff --git a/libburn/options.h b/libburn/options.h index aebfdb5..1690767 100644 --- a/libburn/options.h +++ b/libburn/options.h @@ -32,6 +32,11 @@ struct burn_write_opts /** Perform calibration of the drive's laser before beginning the write. */ unsigned int perform_opc:1; + + /* ts A61219 : Output block size to trigger buffer flush if hit. + -1 with CD, 32 kB with DVD */ + int obs; + /** A disc can have a media catalog number */ int has_mediacatalog; unsigned char mediacatalog[13]; diff --git a/libburn/sector.c b/libburn/sector.c index 0fc8a90..6fcceab 100644 --- a/libburn/sector.c +++ b/libburn/sector.c @@ -217,7 +217,8 @@ static unsigned char *get_sector(struct burn_write_opts *opts, return NULL; seclen += burn_subcode_length(outmode); - if (out->bytes + (seclen) >= BUFFER_SIZE) { + /* ts A61219 : opts->obs is eventually a 32k trigger for DVD */ + if (out->bytes + (seclen) > BUFFER_SIZE || out->bytes == opts->obs) { int err; err = d->write(d, d->nwa, out); if (err == BE_CANCELLED) @@ -642,7 +643,10 @@ int sector_data(struct burn_write_opts *o, struct burn_track *t, int psub) return 2; } - if (!t->source->read_sub) + /* ts A61219 : allow track without .entry */ + if (t->entry == NULL) + ; + else if (!t->source->read_sub) subcode_user(o, subs, t->entry->point, t->entry->control, 1, &t->isrc, psub); else if (!t->source->read_sub(t->source, subs, 96)) diff --git a/libburn/transport.h b/libburn/transport.h index 3bce299..71a9b2b 100644 --- a/libburn/transport.h +++ b/libburn/transport.h @@ -36,7 +36,12 @@ struct params struct buffer { - unsigned char data[BUFFER_SIZE]; + /* ts A61219: + Added 4096 bytes reserve against possible buffer overflows. + (Changed in sector.c buffer flush test from >= to > BUFFER_SIZE . + This can at most cause a 1 sector overlap. Sometimes an offset + of 16 byte is applied to the output data (in some RAW mode). ) */ + unsigned char data[BUFFER_SIZE + 4096]; int sectors; int bytes; }; @@ -124,6 +129,11 @@ struct burn_drive int current_profile; char current_profile_text[80]; int current_is_cd_profile; + int current_is_supported_profile; + + /* ts A61218 from 46h GET CONFIGURATION */ + int bg_format_status; /* 0=needs format start, 1=needs format restart*/ + volatile int released; @@ -206,6 +216,9 @@ struct burn_drive /* ts A61023 : get size and free space of drive buffer */ int (*read_buffer_capacity) (struct burn_drive *d); + /* ts A61220 : format media (e.g. DVD+RW) */ + int (*format_unit) (struct burn_drive *d); + }; /* end of generic 'drive' data structures */ diff --git a/libburn/write.c b/libburn/write.c index 632619e..9ec2b2c 100644 --- a/libburn/write.c +++ b/libburn/write.c @@ -580,6 +580,30 @@ ex:; return ret; } + +/* ts A61218 : outsourced from burn_write_track() */ +int burn_disc_init_track_status(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_drive *d = o->drive; + int sectors; + + /* ts A61102 */ + d->busy = BURN_DRIVE_WRITING; + + /* Update progress */ + d->progress.start_sector = d->nwa; + d->progress.sectors = sectors; + d->progress.sector = 0; + + /* ts A60831: added tnum-line, extended print message on proposal + by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */ + d->progress.track = tnum; + + return 1; +} + + int burn_write_track(struct burn_write_opts *o, struct burn_session *s, int tnum) { @@ -637,20 +661,10 @@ int burn_write_track(struct burn_write_opts *o, struct burn_session *s, /* user data */ - /* ts A61102 */ - d->busy = BURN_DRIVE_WRITING; - sectors = burn_track_get_sectors(t); open_ended = burn_track_is_open_ended(t); - /* Update progress */ - d->progress.start_sector = d->nwa; - d->progress.sectors = sectors; - d->progress.sector = 0; - - /* ts A60831: added tnum-line, extended print message on proposal - by bonfire-app@wanadoo.fr in http://libburn.pykix.org/ticket/58 */ - d->progress.track = tnum; + burn_disc_init_track_status(o, s, tnum); burn_print(12, "track %d is %d sectors long\n", tnum, sectors); @@ -761,47 +775,12 @@ bad_track_mode_found:; return 0; } -void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc) + +/* ts A61218 : outsourced from burn_disc_write_sync() */ +int burn_disc_init_write_status(struct burn_write_opts *o, + struct burn_disc *disc) { - struct cue_sheet *sheet; struct burn_drive *d = o->drive; - struct buffer buf; - struct burn_track *lt; - int first = 1, i, ret, lba, nwa = 0; - char msg[80]; - -/* ts A60924 : libburn/message.c gets obsoleted - burn_message_clear_queue(); -*/ - - burn_print(1, "sync write of %d sessions\n", disc->sessions); - d->buffer = &buf; - memset(d->buffer, 0, sizeof(struct buffer)); - - d->rlba = -150; - - d->toc_temp = 9; - -/* Apparently some drives require this command to be sent, and a few drives -return crap. so we send the command, then ignore the result. -*/ - /* ts A61107 : moved up send_write_parameters because LG GSA-4082B - seems to dislike get_nwa() in advance */ - d->alba = d->start_lba; /* ts A61114: this looks senseless */ - d->nwa = d->alba; - if (o->write_type == BURN_WRITE_TAO) { - nwa = 0; /* get_nwa() will be called in burn_track() */ - } else { - - d->send_write_parameters(d, o); - - ret = d->get_nwa(d, -1, &lba, &nwa); - sprintf(msg, "Inquired nwa: %d (ret=%d)", nwa, ret); - libdax_msgs_submit(libdax_messenger, d->global_index, - 0x00000002, - LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, - msg,0,0); - } /* init progress before showing the state */ d->progress.session = 0; @@ -825,6 +804,259 @@ return crap. so we send the command, then ignore the result. d->busy = BURN_DRIVE_WRITING; + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_track(struct burn_write_opts *o, + struct burn_session *s, int tnum) +{ + struct burn_track *t = s->track[tnum]; + struct burn_drive *d = o->drive; + struct buffer *out = d->buffer; + int sectors; + int i, open_ended = 0, ret= 0; + + sectors = burn_track_get_sectors(t); + open_ended = burn_track_is_open_ended(t); + + /* >>> any type specific track preparations */; + + burn_disc_init_track_status(o, s, tnum); + for (i = 0; open_ended || i < sectors; i++) { + + /* From time to time inquire drive buffer */ + if ((i%256)==0) + d->read_buffer_capacity(d); + + /* transact a (CD sized) sector */ + if (!sector_data(o, t, 0)) + { ret = 0; goto ex; } + + if (open_ended) { + d->progress.sectors = sectors = i; + if (burn_track_is_data_done(t)) + break; + } + + /* update current progress */ + d->progress.sector++; + } + + /* Pad up buffer to next full 32 kB */ + if (out->bytes > 0 && out->bytes < o->obs) { + memset(out->data + out->bytes, 0, o->obs - out->bytes); + out->sectors += (o->obs - out->bytes) / 2048; + out->bytes = o->obs; + } + ret = burn_write_flush(o, t); + if (ret <= 0) + goto ex; + + /* >>> any normal track finalizing */; + + ret = 1; +ex:; + if (ret<=0) { + /* >>> any unnormal track finalizing */; + } + return ret; +} + + +/* ts A61219 */ +int burn_disc_close_session_dvd_plus_rw(struct burn_write_opts *o, + struct burn_session *s) +{ + struct burn_drive *d = o->drive; + + /* This seems to be a quick end : "if (!dvd_compat)" */ + /* >>> Stop de-icing (ongoing background format) quickly + by mmc_close() i(but with opcode[2]=0). + Wait for unit to get ready. + return 1; + */ + /* Else: end eventual background format in a "DVD-RO" compatible way */ + d->close_track_session(d, 1, 0); /* same as CLOSE SESSION for CD */ + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_session(struct burn_write_opts *o, + struct burn_session *s) +{ + int i,ret; + struct burn_drive *d = o->drive; + + for (i = 0; i < s->tracks; i++) { + ret = burn_dvd_write_track(o, s, i); + if (ret <= 0) + break; + } + if (strcmp(d->current_profile_text,"DVD+RW")==0) { + ret = burn_disc_close_session_dvd_plus_rw(o, s); + if (ret <= 0) + return 0; + } + return 1; +} + + +/* ts A61218 : learned much from dvd+rw-tools-7.0/growisofs_mmc.cpp */ +int burn_disc_setup_dvd_plus_rw(struct burn_write_opts *o, + struct burn_disc *disc) +{ + struct burn_drive *d = o->drive; + int ret; + + fprintf(stderr, "LIBBURN_DEBUG: d->bg_format_status= %d\n", + d->bg_format_status); + + if (d->bg_format_status==0 || d->bg_format_status==1) { + /* start or re-start dvd_plus_rw formatting */ + ret = d->format_unit(d); + + fprintf(stderr, "LIBBURN_DEBUG: format_unit() = %d\n", ret); + + if (ret <= 0) + return 0; + } + + /* >>> Set speed */; + + /* >>> perform OPC if needed */; + + d->nwa = 0; + /* >>> d->get_nwa() (default to 0) */; + + /* >>> ? what else ? */; + + return 1; +} + + +/* ts A61218 */ +int burn_dvd_write_sync(struct burn_write_opts *o, + struct burn_disc *disc) +{ + int i, ret; + struct burn_drive *d = o->drive; + char msg[160]; + + if (strcmp(d->current_profile_text,"DVD+RW")==0) { + + if (disc->sessions!=1 || disc->session[0]->tracks>1) { + sprintf(msg, + "Burning of DVD+RW is restricted to a single track"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x0002011f, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + + ret = burn_disc_setup_dvd_plus_rw(o, disc); + if (ret <= 0) { + sprintf(msg, + "Write preparation setup failed for DVD+RW"); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00020121, + LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + + } else { + 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, + LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, + msg, 0,0); + return 0; + } + + o->obs = 32*1024; /* buffer flush trigger for sector.c:get_sector() */ + + burn_disc_init_write_status(o, disc); + for (i = 0; i < disc->sessions; i++) { + /* update progress */ + d->progress.session = i; + d->progress.tracks = disc->session[i]->tracks; + + ret = burn_dvd_write_session(o, disc->session[i]); + if (ret <= 0) + goto ex; + } + + /* >>> eventual normal finalization measures */ + + ret = 1; +ex:; + + /* >>> eventual emergency finalization measures */ + + /* update media state records */ + burn_drive_mark_unready(d); + burn_drive_inquire_media(d); + + return ret; +} + + +void burn_disc_write_sync(struct burn_write_opts *o, struct burn_disc *disc) +{ + struct cue_sheet *sheet; + struct burn_drive *d = o->drive; + struct buffer buf; + struct burn_track *lt; + int first = 1, i, ret, lba, nwa = 0; + char msg[80]; + +/* ts A60924 : libburn/message.c gets obsoleted + burn_message_clear_queue(); +*/ + + d->buffer = &buf; + memset(d->buffer, 0, sizeof(struct buffer)); + d->rlba = -150; + d->toc_temp = 9; + + /* ts A61218 */ + if (! d->current_is_cd_profile) { + ret = burn_dvd_write_sync(o, disc); + if (ret <= 0) + goto fail_wo_sync; + return; + } + + burn_print(1, "sync write of %d CD sessions\n", disc->sessions); + +/* Apparently some drives require this command to be sent, and a few drives +return crap. so we send the command, then ignore the result. +*/ + /* ts A61107 : moved up send_write_parameters because LG GSA-4082B + seems to dislike get_nwa() in advance */ + d->alba = d->start_lba; /* ts A61114: this looks senseless */ + d->nwa = d->alba; + if (o->write_type == BURN_WRITE_TAO) { + nwa = 0; /* get_nwa() will be called in burn_track() */ + } else { + + d->send_write_parameters(d, o); + + ret = d->get_nwa(d, -1, &lba, &nwa); + sprintf(msg, "Inquired nwa: %d (ret=%d)", nwa, ret); + libdax_msgs_submit(libdax_messenger, d->global_index, + 0x00000002, + LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_ZERO, + msg,0,0); + } + + burn_disc_init_write_status(o, disc); + for (i = 0; i < disc->sessions; i++) { /* update progress */ d->progress.session = i; @@ -932,6 +1164,7 @@ return crap. so we send the command, then ignore the result. fail: d->sync_cache(d); +fail_wo_sync:; burn_print(1, "done - failed\n"); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002010b, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH,