/* -*- indent-tabs-mode: t; tab-width: 8; c-basic-offset: 8; -*- */ /* Copyright (c) 2004 - 2006 Derek Foreman, Ben Jansens Copyright (c) 2006 - 2014 Thomas Schmitt Provided under GPL version 2 or later. */ #ifdef HAVE_CONFIG_H #include "../config.h" #endif #include #include #include /* ts A61007 */ /* #include */ #include #include #include #include #include /* ts B41126 : O_BINARY is needed for Cygwin but undefined elsewhere */ #ifndef O_BINARY #define O_BINARY 0 #endif #include "sector.h" #include "libburn.h" #include "drive.h" #include "transport.h" /* ts A60925 : obsoleted by libdax_msgs.h #include "message.h" */ #include "crc.h" #include "debug.h" #include "init.h" #include "toc.h" #include "util.h" #include "mmc.h" #include "sg.h" #include "read.h" #include "options.h" /* ts A70812 */ #include "error.h" #include "libdax_msgs.h" extern struct libdax_msgs *libdax_messenger; void burn_disc_read(struct burn_drive *d, const struct burn_read_opts *o) { #if 0 int i, end, maxsects, finish; int seclen; int drive_lba; unsigned short crc; unsigned char fakesub[96]; struct buffer page; <- needs to become dynamic memory int speed; /* ts A61007 : if this function gets revived, then these tests have to be done more graceful */ a ssert((o->version & 0xfffff000) == (OPTIONS_VERSION & 0xfffff000)); a ssert(!d->busy); a ssert(d->toc->valid); a ssert(o->datafd != -1); /* moved up from spc_select_error_params alias d->send_parameters() */ a ssert(d->mdata->valid); /* XXX not sure this is a good idea. copy it? */ /* XXX also, we have duplicated data now, do we remove the fds from struct drive, or only store a subset of the _opts structs in drives */ /* set the speed on the drive */ speed = o->speed > 0 ? o->speed : d->mdata->max_read_speed; d->set_speed(d, speed, 0); d->params.retries = o->hardware_error_retries; d->send_parameters(d, o); d->cancel = 0; d->busy = BURN_DRIVE_READING; d->currsession = 0; /* drive_lba = 232000; d->currtrack = 18; */ d->currtrack = 0; drive_lba = 0; /* XXX removal of this line obviously breaks * d->track_end = burn_track_end(d, d->currsession, d->currtrack);*/ printf("track ends at %d\n", d->track_end); page.sectors = 0; page.bytes = 0; if (o->subfd != -1) { memset(fakesub, 0xFF, 12); memset(fakesub + 12, 0, 84); fakesub[13] = 1; fakesub[14] = 1; fakesub[20] = 2; fakesub[12] = (d->toc->toc_entry[0].control << 4) + d->toc->toc_entry[0].adr; #ifdef Libburn_no_crc_C crc = 0; /* dummy */ #else crc = crc_ccitt(fakesub + 12, 10); #endif fakesub[22] = crc >> 8; fakesub[23] = crc & 0xFF; write(o->subfd, fakesub, 96); } while (1) { seclen = burn_sector_length_read(d, o); for (i = 0; i < page.sectors; i++) { burn_packet_process(d, page.data + seclen * i, o); d->track_end--; drive_lba++; } if ((d->cancel) || (drive_lba == LAST_SESSION_END(d))) { d->busy = BURN_DRIVE_IDLE; if (!d->cancel) d->toc->complete = 1; return; } /* XXX: removal of this line obviously breaks * end = burn_track_end(d, d->currsession, d->currtrack); */ if (drive_lba == end) { d->currtrack++; if (d->currtrack > d->toc->session[d->currsession].lasttrack) { d->currsession++; /* session switch to d->currsession */ /* skipping a lead out */ drive_lba = CURRENT_SESSION_START(d); /* XXX more of the same end = burn_track_end(d, d->currsession, d->currtrack); */ } } page.sectors = 0; page.bytes = 0; maxsects = BUFFER_SIZE / seclen; finish = end - drive_lba; d->track_end = finish; page.sectors = (finish < maxsects) ? finish : maxsects; printf("reading %d sectors from %d\n", page.sectors, drive_lba); /* >>> ts A61009 : ensure page.sectors >= 0 before calling */ /* >>> ts B21123 : Would now be d->read_cd() with with sectype = 0 , mainch = 0xf8 */ d->r ead_sectors(d, drive_lba, page.sectors, o, &page); printf("Read %d\n", page.sectors); } #endif } int burn_sector_length_read(struct burn_drive *d, const struct burn_read_opts *o) { int dlen = 2352; int data; /*XXX how do we handle this crap now?*/ /* data = d->toc->track[d->currtrack].toc_entry->control & 4;*/ data = 1; if (o->report_recovered_errors) dlen += 294; if ((o->subcodes_data) && data) dlen += 96; if ((o->subcodes_audio) && !data) dlen += 96; return dlen; } static int bitcount(unsigned char *data, int n) { int i, j, count = 0; unsigned char tem; for (i = 0; i < n; i++) { tem = data[i]; for (j = 0; j < 8; j++) { count += tem & 1; tem >>= 1; } } return count; } void burn_packet_process(struct burn_drive *d, unsigned char *data, const struct burn_read_opts *o) { unsigned char sub[96]; int ptr = 2352, i, j, code, fb; int audio = 1; #ifndef Libburn_no_crc_C unsigned short crc; #endif if (o->c2errors) { fb = bitcount(data + ptr, 294); if (fb) { /* bitcount(data + ptr, 294) damaged bits */; } ptr += 294; } /* if (d->toc->track[d->currtrack].mode == BURN_MODE_UNINITIALIZED) { if ((d->toc->track[d->currtrack].toc_entry->control & 4) == 0) d->toc->track[d->currtrack].mode = BURN_MODE_AUDIO; else switch (data[15]) { case 0: d->toc->track[d->currtrack].mode = BURN_MODE0; break; case 1: d->toc->track[d->currtrack].mode = BURN_MODE1; break; case 2: d->toc->track[d->currtrack].mode = BURN_MODE2_FORMLESS; break; } } */ if ((audio && o->subcodes_audio) || (!audio && o->subcodes_data)) { memset(sub, 0, sizeof(sub)); for (i = 0; i < 12; i++) { for (j = 0; j < 8; j++) { for (code = 0; code < 8; code++) { sub[code * 12 + i] <<= 1; if (data[ptr + j + i * 8] & (1 << (7 - code))) sub[code * 12 + i]++; } } } #ifndef Libburn_no_crc_C crc = (*(sub + 22) << 8) + *(sub + 23); if (crc != crc_ccitt(sub + 12, 10)) { /* burn_print(1, "sending error on %s %s\n", d->idata->vendor, d->idata->product); e = burn_error(); e->drive = d; burn_print(1, "crc mismatch in Q\n"); */; } #endif /* else process_q(d, sub + 12); */ /* if (o->subfd != -1) write(o->subfd, sub, 96); */ } /* if ((d->track_end <= 150) && (drive_lba + 150 < CURRENT_SESSION_END(d)) && (TOC_ENTRY(d->toc, d->currtrack).control == 4) && (TOC_ENTRY(d->toc, d->currtrack + 1).control == 0)) { burn_print(12, "pregap : %d\n", d->track_end); write(o->binfd, zeros, 2352); #warning XXX WHERE ARE MY SUBCODES } else *//* write(o->datafd, data, 2352); */ } /* so yeah, when you uncomment these, make them write zeros instead of crap static void write_empty_sector(int fd) { static char sec[2352], initialized = 0; if (!initialized) { memset(sec, 0, 2352); initialized = 1; } burn_print(1, "writing an 'empty' sector\n"); write(fd, sec, 2352); } static void write_empty_subcode(int fd) { char sub[96]; write(fd, sub, 96); } static void flipq(unsigned char *sub) { *(sub + 12 + 10) = ~*(sub + 12 + 10); *(sub + 12 + 11) = ~*(sub + 12 + 11); } */ /** @param flag bit1= be silent on failure bit5= report failure with severity DEBUG */ static int burn_stdio_seek(int fd, off_t byte_address, struct burn_drive *d, int flag) { char msg[80]; if (lseek(fd, byte_address, SEEK_SET) != -1) return 1; if (!(flag & 2)) { sprintf(msg, "Cannot address start byte %.f", (double) byte_address); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020147, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, errno, 0); } return 0; } /* ts A70904 */ /** @param flag bit0= be silent on data shortage bit5= report data shortage with severity DEBUG */ int burn_stdio_read(int fd, char *buf, int bufsize, struct burn_drive *d, int flag) { int todo, count = 0; for(todo = bufsize; todo > 0; ) { count = read(fd, buf + (bufsize - todo), todo); if(count <= 0) break; todo -= count; } if(todo > 0 && !(flag & 1)) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002014a, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Cannot read desired amount of data", errno, 0); } if (count < 0) return -1; return (bufsize - todo); } /* With DVD and BD media, the minimum ECC entity is read instead of single blocks. @param flag see burn_read_data() in libburn.h */ static int retry_mmc_read(struct burn_drive *d, int chunksize, int sose_mem, int start, char **wpt, off_t *data_count, int flag) { int i, err, todo; int retry_at, retry_size; retry_at = start; retry_size = chunksize; todo = chunksize; retry_size = 16; /* DVD ECC block size */ if (d->current_is_cd_profile) { retry_size = 1; /* CD block size */ } else if (d->current_profile >= 0x40 && d->current_profile <= 0x43) { retry_size = 32; /* BD cluster size */ } for (i = 0; todo > 0; i++) { if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; retry_at = start + i * retry_size; if (retry_size > todo) retry_size = todo; err = d->read_10(d, retry_at, retry_size, d->buffer); if (flag & (2 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) return 0; memcpy(*wpt, d->buffer->data, retry_size * 2048); *wpt += retry_size * 2048; *data_count += retry_size * 2048; todo -= retry_size; } return 1; } /* @param flag see burn_read_data() in libburn.h */ static int retry_stdio_read(struct burn_drive *d, int fd, int chunksize, int start, char **wpt, off_t *data_count, int flag) { int i, ret, to_read, todo; ret = burn_stdio_seek(fd, ((off_t) start) * 2048, d, flag & 2); if (ret <= 0) return ret; todo = chunksize * 2048; for (i = 0; todo > 0; i += 2048) { to_read = todo; if (to_read > 2048) to_read = 2048; ret = burn_stdio_read(fd, (char *) d->buffer->data, to_read, d, 1); if (ret <= 0) return 0; memcpy(*wpt, d->buffer->data, to_read); *wpt += to_read; *data_count += to_read; todo -= to_read; } return 1; } /* ts A70812 : API function */ int burn_read_data(struct burn_drive *d, off_t byte_address, char data[], off_t data_size, off_t *data_count, int flag) { int alignment = 2048, start, upto, chunksize = 1, err, cpy_size; int sose_mem = 0, fd = -1, ret; char msg[81], *wpt; struct buffer *buf = NULL, *buffer_mem = d->buffer; /* #define Libburn_read_data_adr_logginG 1 */ #ifdef Libburn_read_data_adr_logginG static FILE *log_fp= NULL; if(log_fp == NULL) log_fp = fopen("/tmp/burn_read_data_log", "a"); if(log_fp!=NULL) fprintf(log_fp, "%d\n", (int) (byte_address / 2048)); #endif /* Libburn_read_data_logginG */ BURN_ALLOC_MEM(buf, struct buffer, 1); *data_count = 0; sose_mem = d->silent_on_scsi_error; if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020142, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on random access read", 0, 0); {ret = 0; goto ex;} } if (d->drive_role == 0) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (null-drive)", 0, 0); {ret = 0; goto ex;} } else if (d->drive_role == 3) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020151, LIBDAX_MSGS_SEV_FAILURE, LIBDAX_MSGS_PRIO_HIGH, "Read attempt on write-only drive", 0, 0); {ret = 0; goto ex;} } if ((byte_address % alignment) != 0) { sprintf(msg, "Read start address not properly aligned (%d bytes)", alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020143, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if (d->media_read_capacity != 0x7fffffff && byte_address >= ((off_t) d->media_read_capacity + (off_t) 1) * (off_t) 2048) { if (!(flag & 2)) { sprintf(msg, "Read start address %ds larger than number of readable blocks %d", (int) (byte_address / 2048 + !!(byte_address % 2048)), d->media_read_capacity + 1); libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020172, (flag & 32) ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); } {ret = 0; goto ex;} } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020145, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to read data", 0, 0); {ret = 0; goto ex;} } if (d->drive_role != 1) { /* <<< We need _LARGEFILE64_SOURCE defined by the build system. */ #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif fd = d->stdio_fd; if (fd < 0) d->stdio_fd = fd = open(d->devname, O_RDONLY | O_LARGEFILE | O_BINARY); if (fd == -1) { if (errno == EACCES && (flag & 2)) { if (!(flag & 8)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020183, LIBDAX_MSGS_SEV_WARNING, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive) for reading", errno, 0); } else if (errno != ENOENT || !(flag & 2)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020005, (flag & 32) && errno == ENOENT ? LIBDAX_MSGS_SEV_DEBUG : LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, "Failed to open device (a pseudo-drive) for reading", errno, 0); ret = 0; if (errno == EACCES && (flag & 8)) ret= -2; goto ex; } ret = burn_stdio_seek(fd, byte_address, d, flag & (2 | 32)); if (ret <= 0) goto ex; } d->busy = BURN_DRIVE_READING_SYNC; d->buffer = buf; start = byte_address / 2048; upto = start + data_size / 2048; if (data_size % 2048) upto++; wpt = data; for (; start < upto; start += chunksize) { chunksize = upto - start; if (chunksize > (BUFFER_SIZE / 2048)) { chunksize = (BUFFER_SIZE / 2048); cpy_size = BUFFER_SIZE; } else cpy_size = data_size - *data_count; if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; if (flag & 16) { d->had_particular_error &= ~1; if (!d->silent_on_scsi_error) d->silent_on_scsi_error = 2; } if (d->drive_role == 1) { err = d->read_10(d, start, chunksize, d->buffer); } else { ret = burn_stdio_read(fd, (char *) d->buffer->data, cpy_size, d, (flag & 32) | !!(flag & 2)); err = 0; if (ret <= 0) err = BE_CANCELLED; } if (flag & (2 | 16 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) { if ((flag & 16) && (d->had_particular_error & 1)) {ret = -3; goto ex;} /* Retry: with CD read by single blocks with other media: retry in full chunks */ if(flag & 4) goto bad_read; if (d->drive_role == 1) { ret = retry_mmc_read(d, chunksize, sose_mem, start, &wpt, data_count, flag); } else { ret = retry_stdio_read(d, fd, chunksize, start, &wpt, data_count, flag); } if (ret <= 0) goto bad_read; } else { memcpy(wpt, d->buffer->data, cpy_size); wpt += cpy_size; *data_count += cpy_size; } } ret = 1; ex:; BURN_FREE_MEM(buf); d->buffer = buffer_mem; d->busy = BURN_DRIVE_IDLE; return ret; bad_read:; if (!(flag & 2)) libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020000, LIBDAX_MSGS_SEV_DEBUG, LIBDAX_MSGS_PRIO_HIGH, "burn_read_data() returns 0", 0, 0); ret = 0; goto ex; } /* ts B21119 : API function*/ int burn_read_audio(struct burn_drive *d, int sector_no, char data[], off_t data_size, off_t *data_count, int flag) { int alignment = 2352, start, upto, chunksize = 1, err, cpy_size, i; int sose_mem = 0, ret; char msg[81], *wpt; struct buffer *buf = NULL, *buffer_mem = d->buffer; BURN_ALLOC_MEM(buf, struct buffer, 1); *data_count = 0; sose_mem = d->silent_on_scsi_error; if (d->released) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020142, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is not grabbed on random access read", 0, 0); {ret = 0; goto ex;} } if (d->drive_role != 1) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020146, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is a virtual placeholder (stdio-drive or null-drive)", 0, 0); {ret = 0; goto ex;} } if ((data_size % alignment) != 0) { sprintf(msg, "Audio read size not properly aligned (%d bytes)", alignment); libdax_msgs_submit(libdax_messenger, d->global_index, 0x0002019d, LIBDAX_MSGS_SEV_SORRY, LIBDAX_MSGS_PRIO_HIGH, msg, 0, 0); {ret = 0; goto ex;} } if (d->busy != BURN_DRIVE_IDLE) { libdax_msgs_submit(libdax_messenger, d->global_index, 0x00020145, LIBDAX_MSGS_SEV_FATAL, LIBDAX_MSGS_PRIO_HIGH, "Drive is busy on attempt to read audio", 0, 0); {ret = 0; goto ex;} } d->busy = BURN_DRIVE_READING_SYNC; d->buffer = buf; start = sector_no; upto = start + data_size / alignment; wpt = data; for (; start < upto; start += chunksize) { chunksize = upto - start; if (chunksize > (BUFFER_SIZE / alignment)) chunksize = (BUFFER_SIZE / alignment); cpy_size = chunksize * alignment; if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; if (flag & 16) { d->had_particular_error &= ~1; if (!d->silent_on_scsi_error) d->silent_on_scsi_error = 2; } err = d->read_cd(d, start, chunksize, 1, 0x10, NULL, d->buffer, (flag & 8) >> 3); if (flag & (2 | 16 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) { if ((flag & 16) && (d->had_particular_error & 1)) {ret = -3; goto ex;} if(!(flag & 4)) for (i = 0; i < chunksize - 1; i++) { if (flag & 2) d->silent_on_scsi_error = 1; else if (flag & 32) d->silent_on_scsi_error = 3; err = d->read_cd(d, start + i, 1, 1, 0x10, NULL, d->buffer, (flag & 8) >> 3); if (flag & (2 | 32)) d->silent_on_scsi_error = sose_mem; if (err == BE_CANCELLED) break; memcpy(wpt, d->buffer->data, alignment); wpt += alignment; *data_count += alignment; } ret = 0; goto ex; } memcpy(wpt, d->buffer->data, cpy_size); wpt += cpy_size; *data_count += cpy_size; } ret = 1; ex: BURN_FREE_MEM(buf); d->buffer = buffer_mem; d->busy = BURN_DRIVE_IDLE; return ret; } #ifdef Libburn_develop_quality_scaN /* B21108 ts */ int burn_nec_optiarc_rep_err_rate(struct burn_drive *d, int start_lba, int rate_period, int flag) { int ret, lba = 0, error_rate1 = 0, error_rate2 = 0, enabled = 0, dret; /* Sub Operation Code 1 : Enable Error Rate reporting function */ ret = mmc_nec_optiarc_f3(d, 1, start_lba, rate_period, &lba, &error_rate1, &error_rate2); if (ret <= 0) goto ex; enabled = 1; /* >>> Sub Operation Code 2 : Seek to starting address start_lba , rate_period */; /* >>> Loop with Sub Operation Code 3 : Send Error Rate information reply: 4-byte LBA , 2-byte C1/PIE , 2-byte C2/PIF */; ret = 1; ex:; if (enabled) { /* Code F : Disable Error Rate reporting function */ dret = mmc_nec_optiarc_f3(d, 0xf, 0, 0, &lba, &error_rate1, &error_rate2); if (dret < ret) ret = dret; } return ret; } #endif /* Libburn_develop_quality_scaN */