/* cdrdao - write audio CD-Rs in disc-at-once mode * * Copyright (C) 1998-2001 Andreas Mueller * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include "TeacCdr55.h" #include "Toc.h" #include "PQSubChannel16.h" #include "util.h" TeacCdr55::TeacCdr55(ScsiIf *scsiIf, unsigned long options) : CdrDriver(scsiIf, options) { int i; driverName_ = "Teac CD-R50/55 - Version 0.1 (data)"; speed_ = 0; simulate_ = 1; encodingMode_ = 1; scsiTimeout_ = 0; actMode_ = TrackData::AUDIO; writeEndLba_ = 0; memset(modeSelectData_, 0, 12); memset(&diskInfo_, 0, sizeof(DiskInfo)); for (i = 0; i < maxScannedSubChannels_; i++) scannedSubChannels_[i] = new PQSubChannel16; // reads little endian samples audioDataByteOrder_ = 0; } TeacCdr55::~TeacCdr55() { int i; for (i = 0; i < maxScannedSubChannels_; i++) { delete scannedSubChannels_[i]; scannedSubChannels_[i] = NULL; } } // static constructor CdrDriver *TeacCdr55::instance(ScsiIf *scsiIf, unsigned long options) { return new TeacCdr55(scsiIf, options); } // sets speed // return: 0: OK // 1: illegal speed int TeacCdr55::speed(int s) { if (s >= 0 && s <= 4 && s != 3) { speed_ = s; } else if (s == 3) { speed_ = 2; } else if (s > 4) { speed_ = 4; } else { return 1; } return 0; } // loads ('unload' == 0) or ejects ('unload' == 1) tray // return: 0: OK // 1: scsi command failed int TeacCdr55::loadUnload(int unload) const { unsigned char cmd[6]; memset(cmd, 0, 6); cmd[0] = 0x1b; // START/STOP UNIT cmd[4] = unload ? 0x02 : 0x03; if (sendCmd(cmd, 6, NULL, 0, NULL, 0) != 0) { message(-2, "Cannot load/unload medium."); return 1; } return 0; } // Retrieves mode select header and block descriptor and stores it in // class member 'modeSelectData_' for later usage with various mode select // commands. // Return: 0: OK, 1: SCSI error int TeacCdr55::getModeSelectData() { unsigned char cmd[6]; memset(cmd, 0, 6); memset(modeSelectData_, 0, 12); cmd[0] = 0x1a; // MODE SENSE cmd[4] = 12; if (sendCmd(cmd, 6, NULL, 0, modeSelectData_, 12, 1) != 0) { message(-2, "Mode Sense failed."); return 1; } return 0; } // sets read/write speed and simulation mode // return: 0: OK // 1: scsi command failed int TeacCdr55::setWriteSpeed() { unsigned char mp[4]; mp[0] = 0x31; mp[1] = 2; mp[2] = 0; mp[3] = 0; switch (speed_) { case 1: mp[2] = 0; break; case 2: mp[2] = 1; break; case 0: case 4: mp[2] = 2; break; } if (setModePage6(mp, modeSelectData_, NULL, 1) != 0) { message(-2, "Cannot set speed mode page."); return 1; } return 0; } // Sets write parameters via mode page 0x22. // return: 0: OK // 1: scsi command failed int TeacCdr55::setWriteParameters() { unsigned char mp[9]; memset(mp, 0, 9); mp[0] = 0x22; mp[1] = 7; if (multiSession()) { mp[4] = 4; // session at once, keep session open } else { if (diskInfo_.sessionCnt > 0) mp[4] = 0x84; // session at once and close disk else mp[4] = 2; // disk at once } if (setModePage6(mp, modeSelectData_, NULL, 1) != 0) { message(-2, "Cannot set write method mode page."); return 1; } return 0; } int TeacCdr55::setSimulationMode() { unsigned char mp[3]; mp[0] = 0x21; mp[1] = 1; if (simulate()) mp[2] = 3; else mp[2] = 0; if (setModePage6(mp, modeSelectData_, NULL, 1) != 0) { message(-2, "Cannot set preview write mode page."); return 1; } return 0; } int TeacCdr55::setWriteDensity(TrackData::Mode mode) { long blockLength = blockSize(mode, TrackData::SUBCHAN_NONE); unsigned char cmd[6]; unsigned char data[12]; memset(cmd, 0, 6); cmd[0] = 0x15; // MODE SELECT cmd[1] = 0x10; cmd[4] = 12; memcpy(data, modeSelectData_, 12); data[0] = 0; data[3] = 8; data[9] = 0; switch (mode) { case TrackData::AUDIO: data[4] = 0x04; break; case TrackData::MODE1: case TrackData::MODE1_RAW: data[4] = 0x01; break; case TrackData::MODE2: data[4] = 0xc1; break; case TrackData::MODE2_FORM1: data[4] = 0x81; break; case TrackData::MODE2_FORM2: data[4] = 0x82; break; case TrackData::MODE2_RAW: case TrackData::MODE2_FORM_MIX: data[4] = 0x83; // I'm not sure if this really allows writing of mixed // form 1 and form 2 sectors. break; case TrackData::MODE0: message(-3, "Illegal mode in 'TeacCdr55::setWriteDensity()'."); return 0; break; } data[10] = blockLength >> 8; data[11] = blockLength; message(3, "Changing write density to 0x%02x/0x%02x%02x.", data[4], data[10], data[11]); if (sendCmd(cmd, 6, data, 12, NULL, 0, 1) != 0) { message(-2, "Cannot set density/block size."); return 1; } return 0; } // Judges disc and performs power calibration if possible. // judge: 1: judge disc, 0: don't judge disc // Return 0: OK // 1: judge disc rejected inserted medium // 2: SCSI error occured int TeacCdr55::executeOPC(int judge) { unsigned char cmd[12]; const unsigned char *senseCode; int senseLen; memset(cmd, 0, 12); cmd[0] = 0xec; // OPC EXECUTION if (judge) { cmd[1] = 1; // judge current disc message(2, "Judging disk..."); switch (sendCmd(cmd, 12, NULL, 0, NULL, 0, 0)) { case 0: // OK, disc can be written at selected speed break; case 1: message(-2, "Judge disk command failed."); return 2; break; case 2: // Check sense code senseCode = scsiIf_->getSense(senseLen); if (senseLen > 12 && (senseCode[2] & 0x0f) == 5 && senseCode[7] != 0) { switch (senseCode[12]) { case 0xcd: message(-2, "Cannot ensure reliable writing with inserted disk."); return 1; break; case 0xce: message(-2, "Cannot ensure reliable writing at selected speed."); return 1; break; case 0xcf: message(-2, "Cannot ensure reliable writing - disk has no ID code."); return 1; break; } } scsiIf_->printError(); return 2; break; } } if (simulate()) { message(2, "Skipping optimum power calibration in simulation mode."); return 0; } cmd[1] = 0; message(2, "Performing optimum power calibration..."); if (sendCmd(cmd, 12, NULL, 0, NULL, 0, 1) != 0) { message(-2, "Optimum power calibration failed."); return 2; } return 0; } // Clears the drive's subcode data. // Return: 0: OK, 1: SCSI error int TeacCdr55::clearSubcode() { unsigned char cmd[12]; memset(cmd, 0, 12); cmd[0] = 0xe4; // CLEAR SUBCODE cmd[5] = 0x80; if (sendCmd(cmd, 12, NULL, 0, NULL, 0, 1) != 0) { message(-2, "Clear subcode failed."); return 1; } return 0; } // Sets subcode data. // start: start LBA // end: end LBA, one greater than last block with specfied subcode data // ctl: track mode flags in bits 4-7 // trackNr: track number // indexNr: index number // pflag: P-channel flag // return: 0: OK, 1: SCSI error int TeacCdr55::setSubcode(long start, long end, unsigned char ctl, int trackNr, int indexNr, int pflag) { assert(start < end); unsigned char cmd[12]; unsigned char data[4]; long len = end - start; message(4, "Setting subcode: start: %6ld, end: %6ld, len: %6ld, CTL: %02x P: %d, T: %2d I: %2d", start, end, len, ctl, pflag, trackNr, indexNr); memset(cmd, 0, 12); cmd[0] = 0xb3; // SET LIMITS cmd[2] = start >> 24; cmd[3] = start >> 16; cmd[4] = start >> 8; cmd[5] = start; cmd[6] = len >> 24; cmd[7] = len >> 16; cmd[8] = len >> 8; cmd[9] = len; if (sendCmd(cmd, 12, NULL, 0, NULL, 0, 1) != 0) { message(-2, "Cannot set limits."); return 1; } memset(cmd, 0, 10); cmd[0] = 0xc2; // SET SUBCODE cmd[8] = 4; // parameter list length data[0] = pflag != 0 ? 1 : 0; data[1] = (ctl & 0xf0) | 0x01; data[2] = SubChannel::bcd(trackNr); data[3] = SubChannel::bcd(indexNr); if (sendCmd(cmd, 10, data, 4, NULL, 0, 1) != 0) { message(-2, "Cannot set subcode."); return 1; } return 0; } // Sets the subcodes for given track. // track: actual track // trackNr: number of current track // start: LBA of track start (index 1), pre-gap length is taken from 'track' // end: LBA of track end, same as 'nstart' if next track has no pre-gap // nstart: LBA of next track start (index 1) // Return: 0: OK // 1: SCSI command failed struct SubCodeData { long start; // start LBA int index; // index int pflag; // P channel flag }; int TeacCdr55::setSubcodes(const Track *track, int trackNr, long start, long end, long nstart) { assert(start < end); assert(end <= nstart); unsigned char ctl = trackCtl(track); SubCodeData subCodes[102]; // index 0-99 + P-channel entry + end-entry int n = 0; int i; if (track->start().lba() != 0) { // pre-gap subCodes[n].start = start - track->start().lba(); subCodes[n].index = 0; subCodes[n].pflag = 1; n++; } // track start (index 1) subCodes[n].start = start; subCodes[n].index = 1; subCodes[n].pflag = 0; n++; // create index marks // We have to check if we have to add an entry for the P-channel flag if the // pre-gap of the following track is shorter than 2 seconds // calculate LBA where P-channel must be set for next track, if // it is behind 'end' it'll be ignored long pChangeLba = nstart - 150; int pChangeEntry = 0; // set to 1 if a P-channel entry was created long indexStart; for (i = 0; i < track->nofIndices(); i++) { indexStart = track->getIndex(i).lba() + start; if (indexStart > pChangeLba && pChangeEntry == 0) { subCodes[n].start = pChangeLba; subCodes[n].index = i + 1; subCodes[n].pflag = 1; n++; pChangeEntry = 1; } else if (indexStart == pChangeLba) { // index increment coincides with p channel change position // -> no entry required pChangeEntry = 1; } subCodes[n].start = indexStart; subCodes[n].index = i + 2; subCodes[n].pflag = indexStart >= pChangeLba ? 1 : 0; n++; } if (pChangeEntry == 0 && pChangeLba < end) { subCodes[n].start = pChangeLba; subCodes[n].index = i + 1; subCodes[n].pflag = 1; n++; } // add end of track as last entry subCodes[n].start = end; subCodes[n].index = 0; // not used subCodes[n].pflag = 0; // not used assert(n <= 101); // now issue the subcode commands for (i = 0; i < n; i++) { if (setSubcode(subCodes[i].start, subCodes[i+1].start, ctl, trackNr, subCodes[i].index, subCodes[i].pflag) != 0) { return 1; } } return 0; } // Sets sub codes for all tracks. // Return: 0: OK // 1: toc contains no track // 2: SCSI error int TeacCdr55::setSubcodes() { const Track *t, *nt; int trackNr; Msf start, end; Msf nstart, nend; long offset = diskInfo_.thisSessionLba; int first = 1; TrackIterator itr(toc_); trackNr = diskInfo_.lastTrackNr; if ((nt = itr.first(nstart, nend)) == NULL) { return 1; } if (clearSubcode() != 0) return 2; do { trackNr += 1; t = nt; start = nstart; end = nend; if ((nt = itr.next(nstart, nend)) == NULL) nstart = Msf(toc_->length()); // start of lead-out if (first) { // set sub code for pre-gap of first track first = 0; if (setSubcode(offset - 150, offset, trackCtl(t), trackNr, 0, 1) != 0) return 2; } if (setSubcodes(t, trackNr, start.lba() + offset, end.lba() + offset, nstart.lba() + offset) != 0) { return 2; } } while (nt != NULL); return 0; } // Sets toc data. // Return: 0: OK // 1: toc contains no track // 2: SCSI command failed int TeacCdr55::setToc() { const Track *t; int trackNr; Msf start, end; long offset = diskInfo_.thisSessionLba + 150; // LBA offset int trackOffset = diskInfo_.lastTrackNr + 1; unsigned char firstTrackCtl = 0; unsigned char lastTrackCtl = 0; unsigned char ctl; int lastTrackNr = 0; int sessFormat; unsigned char cmd[10]; unsigned char data[3 + 102 * 5]; // max. 99 tracks + 3 POINT entries unsigned char *p = data + 3; data[0] = 0; data[1] = 0; data[2] = 0; TrackIterator itr(toc_); for (t = itr.first(start, end), trackNr = trackOffset; t != NULL && trackNr <= 99; t = itr.next(start, end), trackNr++) { Msf s(start.lba() + offset); ctl = trackCtl(t); if (trackNr == trackOffset) firstTrackCtl = ctl; p[0] = ctl | 0x01; p[1] = SubChannel::bcd(trackNr); p[2] = SubChannel::bcd(s.min()); p[3] = SubChannel::bcd(s.sec()); p[4] = SubChannel::bcd(s.frac()); p += 5; lastTrackNr = trackNr; lastTrackCtl = ctl; } if (lastTrackNr == 0) return 1; // Point A0 entry (first track nr and session format) if (diskInfo_.empty) { // disk is empty -> take toc type from toc-file sessFormat = sessionFormat(); } else { // Disk has already a recorded session -> take toc type from // previous session if it is well defined. switch (diskInfo_.diskTocType) { case 0x00: case 0x10: case 0x20: sessFormat = diskInfo_.diskTocType; break; default: sessFormat = sessionFormat(); break; } } p[0] = firstTrackCtl | 0x01; p[1] = 0xa0; p[2] = SubChannel::bcd(trackOffset); p[3] = SubChannel::bcd(sessFormat); p[4] = 0; p += 5; // Point A1 entry (last track nr) p[0] = lastTrackCtl | 0x01; p[1] = 0xa1; p[2] = SubChannel::bcd(lastTrackNr); p[3] = 0; p[4] = 0; p += 5; // Point A2 entry (start of lead-out) Msf los(toc_->length().lba() + offset); p[0] = lastTrackCtl | 0x01; p[1] = 0xa2; p[2] = SubChannel::bcd(los.min()); p[3] = SubChannel::bcd(los.sec()); p[4] = SubChannel::bcd(los.frac()); p += 5; long dataLen = p - data; #if 1 message(4, "TOC data:"); long i; for (i = 3; i < dataLen; i += 5) { message(4, "%02x %02x %02x:%02x:%02x", data[i], data[i+1], data[i+2], data[i+3], data[i+4]); } #endif memset(cmd, 0, 10); cmd[0] = 0xc2; // SET SUBCODE cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, data, dataLen, NULL, 0, 1) != 0) { message(-2, "Cannot set TOC data."); return 2; } return 0; } // Sets catalog number. // Return: 0: OK // 1: SCSI command failed int TeacCdr55::setCatalog() { unsigned char cmd[10]; unsigned char data[15]; int i; if (!toc_->catalogValid()) return 0; data[0] = 0; if (toc_->leadInMode() == TrackData::AUDIO) data[1] = 0x02; else data[1] = 0x42; for (i = 0; i < 13; i++) data[2 + i] = toc_->catalog(i); memset(cmd, 0, 10); cmd[0] = 0xc2; // SET SUBCODE cmd[8] = 15; if (sendCmd(cmd, 10, data, 15, NULL, 0, 1) != 0) { message(-2, "Cannot set catalog number data."); return 2; } return 0; } // Sets ISRC codes for all tracks int TeacCdr55::setIsrc() { const Track *t; int trackNr; Msf start, end; int trackOffset = diskInfo_.lastTrackNr + 1; unsigned char cmd[10]; unsigned char data[15]; data[0] = 0; memset(cmd, 0, 10); cmd[0] = 0xc2; // SET SUBCODE cmd[8] = 15; TrackIterator itr(toc_); for (t = itr.first(start, end), trackNr = trackOffset; t != NULL && trackNr <= 99; t = itr.next(start, end), trackNr++) { if (t->isrcValid()) { data[1] = trackCtl(t) | 0x03; data[2] = t->isrcCountry(0); data[3] = t->isrcCountry(1); data[4] = t->isrcOwner(0); data[5] = t->isrcOwner(1); data[6] = t->isrcOwner(2); data[7] = t->isrcYear(0) + '0'; data[8] = t->isrcYear(1) + '0'; data[9] = t->isrcSerial(0) + '0'; data[10] = t->isrcSerial(1) + '0'; data[11] = t->isrcSerial(2) + '0'; data[12] = t->isrcSerial(3) + '0'; data[13] = t->isrcSerial(4) + '0'; data[14] = 0; cmd[6] = trackNr; if (sendCmd(cmd, 10, data, 15, NULL, 0, 1) != 0) { message(-2, "Cannot set ISRC code."); return 2; } } } return 0; } // Need to overload this function to set the WriteExtension flag. It'll // also change the write density if the actual mode changes. int TeacCdr55::writeData(TrackData::Mode mode, TrackData::SubChannelMode sm, long &lba, const char *buf, long len) { assert(blocksPerWrite_ > 0); int writeLen = 0; unsigned char cmd[10]; long blockLength = blockSize(mode, TrackData::SUBCHAN_NONE); #if 0 long sum, i; sum = 0; for (i = 0; i < len * blockLength; i++) { sum += buf[i]; } message(0, "W: %ld: %ld, %ld, %ld", lba, blockLength, len, sum); #endif if (mode != actMode_) { actMode_ = mode; message(3, "Changed write density at LBA %ld.", lba); if (setWriteDensity(actMode_) != 0) { return 1; } } memset(cmd, 0, 10); cmd[0] = 0x2a; // WRITE10 while (len > 0) { writeLen = (len > blocksPerWrite_ ? blocksPerWrite_ : len); cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba; cmd[7] = writeLen >> 8; cmd[8] = writeLen & 0xff; if (lba + writeLen >= writeEndLba_) { // last write command message(4, "Last write command at LBA: %ld", lba); cmd[9] = 0; } else { cmd[9] = 0x80; // extended write } if (sendCmd(cmd, 10, (unsigned char *)buf, writeLen * blockLength, NULL, 0) != 0) { message(-2, "Write data failed."); return 1; } buf += writeLen * blockLength; lba += writeLen; len -= writeLen; } return 0; } int TeacCdr55::initDao(const Toc *toc) { long offset; // LBA offset for this session long n; toc_ = toc; blockLength_ = AUDIO_BLOCK_LEN; blocksPerWrite_ = scsiIf_->maxDataLen() / blockLength_; assert(blocksPerWrite_ > 0); diskInfo(); if (!diskInfo_.valid.empty || !diskInfo_.valid.append) { message(-2, "Cannot determine status of inserted medium."); return 1; } if (!diskInfo_.append) { message(-2, "Inserted medium is not appendable."); return 1; } offset = diskInfo_.thisSessionLba; // allocate buffer for writing zeros n = blocksPerWrite_ * blockLength_; delete[] zeroBuffer_; zeroBuffer_ = new char[n]; memset(zeroBuffer_, 0, n); writeEndLba_ = toc_->length().lba() + offset; actMode_ = toc_->leadInMode(); if (getModeSelectData() != 0 || setWriteDensity(actMode_) != 0 || setSimulationMode() != 0 || setWriteParameters() != 0 || setWriteSpeed() != 0) { return 1; } return 0; } int TeacCdr55::startDao() { scsiTimeout_ = scsiIf_->timeout(3 * 60); if (executeOPC(1) != 0 || setSubcodes() != 0 || setCatalog() != 0 || setToc() != 0 || setIsrc() != 0) { return 1; } long lba = diskInfo_.thisSessionLba - 150; if (writeZeros(actMode_, TrackData::SUBCHAN_NONE, lba, lba + 150, 150) != 0) { return 1; } return 0; } int TeacCdr55::finishDao() { //message(0, "Writing lead-out..."); // wait until writing of lead-out is finished ??? scsiIf_->timeout(scsiTimeout_); delete[] zeroBuffer_, zeroBuffer_ = NULL; return 0; } void TeacCdr55::abortDao() { unsigned char cmd[10]; // Send a zero length write command with cleared extendened write bit. // I don't know if it has the expected effect. memset(cmd, 0, 10); cmd[0] = 0x2a; // WRITE10 sendCmd(cmd, 10, NULL, 0, NULL, 0, 0); } DiskInfo *TeacCdr55::diskInfo() { int i; unsigned char cmd[12]; unsigned char data[12]; memset(&diskInfo_, 0, sizeof(DiskInfo)); diskInfo_.cdrw = 0; diskInfo_.valid.cdrw = 1; memset(cmd, 0, 10); memset(data, 0, 8); cmd[0] = 0x25; // READ CAPACITY cmd[9] = 0x80; if (sendCmd(cmd, 10, NULL, 0, data, 8, 1) != 0) { message(-1, "Cannot read CD-R capacity."); } else { diskInfo_.capacity = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; diskInfo_.valid.capacity = 1; } memset(cmd, 0, 12); memset(data, 0, 4); cmd[0] = 0xe6; // NEXT WRITABLE ADDRESS cmd[2] = 0xff; cmd[3] = 0xff; cmd[4] = 0xff; cmd[5] = 0xff; cmd[6] = 0xff; cmd[7] = 0xff; cmd[8] = 0xff; cmd[9] = 0xff; long nwa = 0; switch (sendCmd(cmd, 12, NULL, 0, data, 4, 0)) { case 0: // command succeed -> writable area exists diskInfo_.empty = 1; diskInfo_.valid.empty = 1; nwa = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; message(4, "Next writable address: %ld", nwa); break; case 1: // SCSI command failed, no sense data -> cannot tell anything message(-1, "Get next writable address failed."); break; case 2: // SCSI command failed, sense data available // assume that disk is not writable message(4, "Cannot get next writable address."); diskInfo_.empty = 0; diskInfo_.valid.empty = 1; break; } // SCSI command failed -> stop further processing if (diskInfo_.valid.empty == 0) return &diskInfo_; memset(cmd, 0, 10); memset(data, 0, 4); cmd[0] = 0x43; // READ TOC cmd[6] = 1; cmd[8] = 4; if (sendCmd(cmd, 10, NULL, 0, data, 4, 0) == 0) { message(4, "First track %u, last track %u", data[2], data[3]); diskInfo_.lastTrackNr = data[3]; } else { message(4, "READ TOC (format 0) failed."); } if (diskInfo_.lastTrackNr > 0) { diskInfo_.empty = 0; diskInfo_.diskTocType = 0xff; // undefined memset(cmd, 0, 10); memset(data, 0, 12); cmd[0] = 0x43; // READ TOC cmd[6] = 0; cmd[8] = 12; cmd[9] = 1 << 6; if (sendCmd(cmd, 10, NULL, 0, data, 12) == 0) { diskInfo_.sessionCnt = data[3]; diskInfo_.lastSessionLba = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11]; message(4, "First session %u, last session %u, last session start %ld", data[2], data[3], diskInfo_.lastSessionLba); } else { message(4, "READ TOC (format 1) failed."); } if (diskInfo_.sessionCnt > 0) { int len; CdRawToc *toc = getRawToc(diskInfo_.sessionCnt, &len); if (toc != NULL) { for (i = 0; i < len; i++) { if (toc[i].sessionNr == diskInfo_.sessionCnt) { if (toc[i].point == 0xb0 && toc[i].min != 0xff && toc[i].sec != 0xff && toc[i].frame != 0xff) { int m = SubChannel::bcd2int(toc[i].min); int s = SubChannel::bcd2int(toc[i].sec); int f = SubChannel::bcd2int(toc[i].frame); if (m < 90 && s < 60 && f < 75) diskInfo_.thisSessionLba = Msf(m, s, f).lba(); // + 150 - 150 } if (toc[i].point == 0xa0) { diskInfo_.diskTocType = SubChannel::bcd2int(toc[i].psec); } } } if (diskInfo_.thisSessionLba > 0) { if (diskInfo_.lastTrackNr < 99) diskInfo_.append = 1; } else { message(4, "Did not find BO pointer in session %d.", diskInfo_.sessionCnt); } delete[] toc; } else { message(4, "getRawToc failed."); } } } else { // disk is empty and appendable diskInfo_.empty = 1; diskInfo_.append = 1; } diskInfo_.valid.append = 1; return &diskInfo_; } // tries to read catalog number from disk and adds it to 'toc' // return: 1 if valid catalog number was found, else 0 int TeacCdr55::readCatalog(Toc *toc, long startLba, long endLba) { unsigned char cmd[10]; unsigned char data[24]; char catalog[14]; int i; memset(cmd, 0, 10); memset(data, 0, 24); cmd[0] = 0x42; // READ SUB-CHANNEL cmd[2] = 1 << 6; cmd[3] = 0x02; // get media catalog number cmd[8] = 24; // transfer length if (sendCmd(cmd, 10, NULL, 0, data, 24) != 0) { message(-2, "Cannot get catalog number."); return 0; } if (data[8] & 0x80) { for (i = 0; i < 13; i++) { catalog[i] = data[0x09 + i]; } catalog[13] = 0; if (toc->catalog(catalog) == 0) { return 1; } } return 0; } int TeacCdr55::readIsrc(int trackNr, char *buf) { unsigned char cmd[10]; unsigned char data[24]; int i; buf[0] = 0; memset(cmd, 0, 10); memset(data, 0, 24); cmd[0] = 0x42; // READ SUB-CHANNEL cmd[2] = 1 << 6; cmd[3] = 0x03; // get media catalog number cmd[6] = trackNr; cmd[8] = 24; // transfer length if (sendCmd(cmd, 10, NULL, 0, data, 24) != 0) { message(-2, "Cannot get ISRC code."); return 0; } if (data[8] & 0x80) { for (i = 0; i < 12; i++) { buf[i] = data[0x09 + i]; } buf[12] = 0; } return 0; } int TeacCdr55::analyzeTrack(TrackData::Mode mode, int trackNr, long startLba, long endLba, Msf *indexIncrements, int *indexIncrementCnt, long *pregap, char *isrcCode, unsigned char *ctl) { int ret = analyzeTrackScan(mode, trackNr, startLba, endLba, indexIncrements, indexIncrementCnt, pregap, isrcCode, ctl); if (mode == TrackData::AUDIO) readIsrc(trackNr, isrcCode); else *isrcCode = 0; return ret; } int TeacCdr55::readSubChannels(TrackData::SubChannelMode, long lba, long len, SubChannel ***chans, Sample *audioData) { int retries = 5; unsigned char cmd[12]; int i; cmd[0] = 0xd8; // READ CD_DA cmd[1] = 0; cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba; cmd[6] = len >> 24; cmd[7] = len >> 16; cmd[8] = len >> 8; cmd[9] = len; cmd[10] = 0x01; // Q sub-channel data cmd[11] = 0; while (1) { if (sendCmd(cmd, 12, NULL, 0, transferBuffer_, len * (AUDIO_BLOCK_LEN + 16), retries == 0 ? 1 : 0) != 0) { if (retries == 0) return 1; } else { break; } retries--; } for (i = 0; i < len; i++) { PQSubChannel16 *chan = (PQSubChannel16*)scannedSubChannels_[i]; unsigned char *p = transferBuffer_ + i * (AUDIO_BLOCK_LEN + 16) + AUDIO_BLOCK_LEN; // slightly reorder the sub-channel so it is suitable for 'PQSubChannel16' p[0] <<= 4; // ctl/adr p[0] |= p[1]; p[1] = p[2]; // track nr p[2] = p[3]; // index nr p[3] = p[4]; // min p[4] = p[5]; // sec p[5] = p[6]; // frame p[6] = 0; // zero // p[7] is amin // p[8] is asec // p[9] is aframe chan->init(p); if (chan->checkConsistency()) chan->calcCrc(); } if (audioData != NULL) { unsigned char *p = transferBuffer_; for (i = 0; i < len; i++) { memcpy(audioData, p, AUDIO_BLOCK_LEN); p += AUDIO_BLOCK_LEN + 16; audioData += SAMPLES_PER_BLOCK; } } *chans = scannedSubChannels_; return 0; } CdRawToc *TeacCdr55::getRawToc(int sessionNr, int *len) { unsigned char cmd[10]; unsigned short dataLen; unsigned char *data = NULL;; unsigned char reqData[4]; // buffer for requestion the actual length unsigned char *p = NULL; int i, entries; CdRawToc *rawToc; assert(sessionNr >= 1); // read disk toc length memset(cmd, 0, 10); cmd[0] = 0x43; // READ TOC cmd[6] = sessionNr; cmd[8] = 4; cmd[9] |= 2 << 6; // get Q subcodes if (sendCmd(cmd, 10, NULL, 0, reqData, 4) != 0) { message(-2, "Cannot read raw disk toc."); return NULL; } dataLen = ((reqData[0] << 8) | reqData[1]) + 2; message(4, "Raw toc data len: %d", dataLen); if (dataLen == 4) return NULL; data = new unsigned char[dataLen]; // read disk toc cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, NULL, 0, data, dataLen) != 0) { message(-2, "Cannot read raw disk toc."); delete[] data; return NULL; } entries = (((data[0] << 8) | data[1]) - 2) / 11; rawToc = new CdRawToc[entries]; for (i = 0, p = data + 4; i < entries; i++, p += 11 ) { #if 0 message(0, "%d %02x %02d %2x %02x:%02x:%02x %02x %02x:%02x:%02x", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10]); #endif rawToc[i].sessionNr = p[0]; rawToc[i].adrCtl = p[1]; rawToc[i].point = p[3]; rawToc[i].min = p[4]; rawToc[i].sec = p[5]; rawToc[i].frame = p[6]; rawToc[i].pmin = p[8]; rawToc[i].psec = p[9]; rawToc[i].pframe = p[10]; } delete[] data; *len = entries; return rawToc; } long TeacCdr55::readTrackData(TrackData::Mode mode, TrackData::SubChannelMode, long lba, long len, unsigned char *buf) { unsigned char cmd[10]; long blockLen = 2340; long i; TrackData::Mode actMode; int ok = 0; int softError; const unsigned char *sense; int senseLen; if (setBlockSize(blockLen) != 0) return 0; memset(cmd, 0, 10); cmd[0] = 0x28; // READ10 cmd[2] = lba >> 24; cmd[3] = lba >> 16; cmd[4] = lba >> 8; cmd[5] = lba; while (len > 0 && !ok) { cmd[7] = len >> 8; cmd[8] = len; memset(transferBuffer_, 0, len * blockLen); switch (sendCmd(cmd, 10, NULL, 0, transferBuffer_, len * blockLen, 0)) { case 0: ok = 1; break; case 2: softError = 0; sense = scsiIf_->getSense(senseLen); if (senseLen > 0x0c) { if ((sense[2] &0x0f) == 5) { switch (sense[12]) { case 0x63: // end of user area encountered on this track case 0x64: // Illegal mode for this track softError = 1; break; } } else if ((sense[2] & 0x0f) == 3) { // Medium error switch (sense[12]) { case 0x02: // No seek complete, sector not found case 0x11: // L-EC error return -2; break; } } } if (!softError) { scsiIf_->printError(); return -1; } break; default: message(-2, "Read error at LBA %ld, len %ld", lba, len); return -1; break; } if (!ok) { len--; } } unsigned char *sector = transferBuffer_; for (i = 0; i < len; i++) { actMode = determineSectorMode(sector); if (!(actMode == mode || (mode == TrackData::MODE2_FORM_MIX && (actMode == TrackData::MODE2_FORM1 || actMode == TrackData::MODE2_FORM2)) || (mode == TrackData::MODE1_RAW && actMode == TrackData::MODE1) || (mode == TrackData::MODE2_RAW && (actMode == TrackData::MODE2 || actMode == TrackData::MODE2_FORM1 || actMode == TrackData::MODE2_FORM2)))) { return i; } if (buf != NULL) { switch (mode) { case TrackData::MODE1: memcpy(buf, sector + 4, MODE1_BLOCK_LEN); buf += MODE1_BLOCK_LEN; break; case TrackData::MODE2: case TrackData::MODE2_FORM_MIX: memcpy(buf, sector + 4, MODE2_BLOCK_LEN); buf += MODE2_BLOCK_LEN; break; case TrackData::MODE2_FORM1: memcpy(buf, sector + 12, MODE2_FORM1_DATA_LEN); buf += MODE2_FORM1_DATA_LEN; break; case TrackData::MODE2_FORM2: memcpy(buf, sector + 12, MODE2_FORM2_DATA_LEN); buf += MODE2_FORM2_DATA_LEN; break; case TrackData::MODE1_RAW: case TrackData::MODE2_RAW: memcpy(buf, syncPattern, 12); memcpy(buf + 12, sector, 2340); buf += AUDIO_BLOCK_LEN; break; case TrackData::MODE0: case TrackData::AUDIO: message(-3, "TeacCdr55::readTrackData: Illegal mode."); return 0; break; } } sector += blockLen; } return len; } Toc *TeacCdr55::readDiskToc(int session, const char *audioFilename) { Toc *toc = CdrDriver::readDiskToc(session, audioFilename); setBlockSize(MODE1_BLOCK_LEN); return toc; } Toc *TeacCdr55::readDisk(int session, const char *fname) { Toc *toc = CdrDriver::readDisk(session, fname); setBlockSize(MODE1_BLOCK_LEN); return toc; } int TeacCdr55::readAudioRange(ReadDiskInfo *rinfo, int fd, long start, long end, int startTrack, int endTrack, TrackInfo *info) { if (!onTheFly_) { int t; message(1, "Analyzing..."); for (t = startTrack; t <= endTrack; t++) { long totalProgress; totalProgress = t * 1000; totalProgress /= rinfo->tracks; sendReadCdProgressMsg(RCD_ANALYZING, rinfo->tracks, t + 1, 0, totalProgress); message(1, "Track %d...", t + 1); info[t].isrcCode[0] = 0; readIsrc(t + 1, info[t].isrcCode); if (info[t].isrcCode[0] != 0) message(2, "Found ISRC code."); totalProgress = (t + 1) * 1000; totalProgress /= rinfo->tracks; sendReadCdProgressMsg(RCD_ANALYZING, rinfo->tracks, t + 1, 1000, totalProgress); } message(1, "Reading..."); } return CdrDriver::readAudioRangeParanoia(rinfo, fd, start, end, startTrack, endTrack, info); }