/* 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. */ /* Driver for the TaiyoYuden drive created by Henk-Jan Slotboom. * Very similar to the Philips CDD2x00 drives. */ #include #include #include #include "TaiyoYuden.h" #include "SubChannel.h" #include "Toc.h" #include "util.h" TaiyoYuden::TaiyoYuden(ScsiIf *scsiIf, unsigned long options) : PlextorReader(scsiIf, options), CDD2600Base(this) { driverName_ = "Taiyo-Yuden - Version 0.1(alpha)"; leadInLength_ = leadOutLength_ = 0; speed_ = 2; simulate_ = 1; encodingMode_ = 0; audioDataByteOrder_ = 0; // little endian memset(&diskInfo_, 0, sizeof(DiskInfo)); } TaiyoYuden::~TaiyoYuden() { } // static constructor CdrDriver *TaiyoYuden::instance(ScsiIf *scsiIf, unsigned long options) { return new TaiyoYuden(scsiIf, options); } // sets speed // return: 0: OK // 1: illegal speed int TaiyoYuden::speed(int s) { if (s >= 0 && s <= 2) { speed_ = s; return 0; } else if (s > 2) { speed_ = 2; return 0; } else { return 1; } } // loads ('unload' == 0) or ejects ('unload' == 1) tray // return: 0: OK // 1: scsi command failed int TaiyoYuden::loadUnload(int unload) const { unsigned char cmd[10]; memset(cmd, 0, 10); cmd[0] = 0xe7; // MEDIUM LOAD/UNLOAD if (unload) { cmd[8] |= 0x01; } if (sendCmd(cmd, 10, NULL, 0, NULL, 0) != 0) { message(-2, "Cannot load/unload medium."); return 1; } return 0; }; int TaiyoYuden::initDao(const Toc *toc) { long n; toc_ = toc; blockLength_ = AUDIO_BLOCK_LEN; blocksPerWrite_ = scsiIf_->maxDataLen() / blockLength_; assert(blocksPerWrite_ > 0); if (modeSelectBlockSize(blockLength_, 1) != 0 || modeSelectSpeed(-1, speed_, simulate_, 1) != 0 || modeSelectCatalog(toc_) != 0 || readSessionInfo(&leadInLength_, &leadOutLength_, 1) != 0) { return 1; } // allocate buffer for write zeros n = blocksPerWrite_ * blockLength_; delete[] zeroBuffer_; zeroBuffer_ = new char[n]; memset(zeroBuffer_, 0, n); return 0; } int TaiyoYuden::startDao() { long lba = -leadInLength_ - 150; // Value is not really important since the // LBA is not used by 'writeData'. if (writeSession(toc_, multiSession_, 0) != 0) { return 1; } message(2, "Writing lead-in and gap..."); // write lead-in if (writeZeros(toc_->leadInMode(), TrackData::SUBCHAN_NONE, lba, 0, leadInLength_) != 0) { flushCache(); return 1; } // write gap (2 seconds) if (writeZeros(toc_->leadInMode(), TrackData::SUBCHAN_NONE, lba, 0, 150) != 0) { flushCache(); return 1; } message(2, ""); return 0; } int TaiyoYuden::finishDao() { long lba = toc_->length().lba(); message(2, "Writing lead-out..."); // write lead-out if (writeZeros(toc_->leadOutMode(), TrackData::SUBCHAN_NONE, lba, lba + 150, leadOutLength_) != 0) { flushCache(); return 1; } message(2, "\nFlushing cache..."); if (flushCache() != 0) { return 1; } message(2, ""); delete[] zeroBuffer_, zeroBuffer_ = NULL; return 0; } void TaiyoYuden::abortDao() { flushCache(); } // Writes data to target, the block length depends on the actual writing mode // and is stored internally. 'len' is number of blocks to write. // 'lba' specifies the next logical block address for writing and is updated // by this function but not used for writing // return: 0: OK // 1: scsi command failed int TaiyoYuden::writeData(TrackData::Mode mode, TrackData::SubChannelMode sm, long &lba, const char *buf, long len) { assert(blocksPerWrite_ > 0); assert(blockLength_ > 0); assert(mode == TrackData::AUDIO); int nwritten = 0; int writeLen = 0; unsigned char cmd[10]; memset(cmd, 0, 10); cmd[0] = 0x2a; // WRITE1 while (len > 0) { writeLen = (len > blocksPerWrite_ ? blocksPerWrite_ : len); cmd[7] = writeLen >> 8; cmd[8] = writeLen & 0xff; if (sendCmd(cmd, 10, (unsigned char *)(buf + (nwritten * blockLength_)), writeLen * blockLength_, NULL, 0) != 0) { message(-2, "Write data failed."); return 1; } lba += writeLen; len -= writeLen; nwritten += writeLen; } return 0; } // Retrieve disk information. // return: DiskInfo structure or 'NULL' on error DiskInfo *TaiyoYuden::diskInfo() { unsigned char cmd[10]; unsigned long dataLen = 34; unsigned char data[34]; memset(&diskInfo_, 0, sizeof(DiskInfo)); if (readCapacity(&(diskInfo_.capacity), 0) == 0) { diskInfo_.valid.capacity = 1; } if (readSessionInfo(&leadInLength_, &leadOutLength_, 0) == 0) { diskInfo_.valid.manufacturerId = 1; // start time of lead-in diskInfo_.manufacturerId = Msf(450150 - leadInLength_ - 150 ); diskInfo_.empty = 1; } else { diskInfo_.empty = 0; } diskInfo_.valid.empty = 0; // does not work for this drive memset(cmd, 0, 10); memset(data, 0, dataLen); cmd[0] = 0x51; // READ DISK INFORMATION cmd[7] = dataLen >> 8; cmd[8] = dataLen; if (sendCmd(cmd, 10, NULL, 0, data, dataLen, 0) == 0) { diskInfo_.empty = (data[2] & 0x03) == 0 ? 1 : 0; diskInfo_.cdrw = (data[2] & 0x10) != 0 ? 1 : 0; diskInfo_.valid.cdrw = 1; } return &diskInfo_; } // Special toc check for Taiyo-Yuden drives. Every track must have a pre-gap // to create a correct disk. The pre-gap length may be only 1 block. // Only warnings are created. // Return: 0: OK // 1: at least one warning was given // 2: at least on error was given int TaiyoYuden::checkToc(const Toc *toc) { int err = PlextorReader::checkToc(toc); int trackNr; Msf start, end; const Track *t; int warningGiven = 0; TrackIterator itr(toc); for (t = itr.first(start, end), trackNr = 1; t != NULL; t = itr.next(start, end), trackNr++) { if (t->start().lba() == 0 && trackNr > 1) { if (!warningGiven) { warningGiven = 1; message(-1, "Each track must have a minimal pre-gap to create a useful disk."); message(-1, "This is not fulfilled for following track(s):"); } message(-1, "Track %d", trackNr); } } if (warningGiven) { message(0,""); if (err < 1) err = 1; } return err; }