1 /* cdrdao - write audio CD-Rs in disc-at-once mode
2 *
3 * Copyright (C) 1998-2001 Andreas Mueller <andreas@daneb.de>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /* Driver for the TaiyoYuden drive created by Henk-Jan Slotboom.
21 * Very similar to the Philips CDD2x00 drives.
22 */
23
24 #include <config.h>
25
26 #include <string.h>
27 #include <assert.h>
28
29 #include "TaiyoYuden.h"
30 #include "SubChannel.h"
31
32 #include "Toc.h"
33 #include "log.h"
34
TaiyoYuden(ScsiIf * scsiIf,unsigned long options)35 TaiyoYuden::TaiyoYuden(ScsiIf *scsiIf, unsigned long options)
36 : PlextorReader(scsiIf, options), CDD2600Base(this)
37 {
38 driverName_ = "Taiyo-Yuden - Version 0.1(alpha)";
39
40 leadInLength_ = leadOutLength_ = 0;
41 speed_ = 2;
42 simulate_ = true;
43 encodingMode_ = 0;
44
45 audioDataByteOrder_ = 0; // little endian
46
47 memset(&diskInfo_, 0, sizeof(DiskInfo));
48 }
49
~TaiyoYuden()50 TaiyoYuden::~TaiyoYuden()
51 {
52 }
53
54 // static constructor
instance(ScsiIf * scsiIf,unsigned long options)55 CdrDriver *TaiyoYuden::instance(ScsiIf *scsiIf, unsigned long options)
56 {
57 return new TaiyoYuden(scsiIf, options);
58 }
59
60
61 // sets speed
62 // return: 0: OK
63 // 1: illegal speed
speed(int s)64 int TaiyoYuden::speed(int s)
65 {
66 if (s >= 0 && s <= 2) {
67 speed_ = s;
68 return 0;
69 }
70 else if (s > 2) {
71 speed_ = 2;
72 return 0;
73 }
74 else {
75 return 1;
76 }
77 }
78
79 // loads ('unload' == 0) or ejects ('unload' == 1) tray
80 // return: 0: OK
81 // 1: scsi command failed
loadUnload(int unload) const82 int TaiyoYuden::loadUnload(int unload) const
83 {
84 unsigned char cmd[10];
85
86 memset(cmd, 0, 10);
87
88 cmd[0] = 0xe7; // MEDIUM LOAD/UNLOAD
89 if (unload) {
90 cmd[8] |= 0x01;
91 }
92
93 if (sendCmd(cmd, 10, NULL, 0, NULL, 0) != 0) {
94 log_message(-2, "Cannot load/unload medium.");
95 return 1;
96 }
97
98 return 0;
99 };
100
initDao(const Toc * toc)101 int TaiyoYuden::initDao(const Toc *toc)
102 {
103 long n;
104
105 toc_ = toc;
106
107 blockLength_ = AUDIO_BLOCK_LEN;
108 blocksPerWrite_ = scsiIf_->maxDataLen() / blockLength_;
109
110 assert(blocksPerWrite_ > 0);
111
112 if (modeSelectBlockSize(blockLength_, 1) != 0 ||
113 modeSelectSpeed(-1, speed_, simulate_, 1) != 0 ||
114 modeSelectCatalog(toc_) != 0 ||
115 readSessionInfo(&leadInLength_, &leadOutLength_, 1) != 0) {
116 return 1;
117 }
118
119 // allocate buffer for write zeros
120 n = blocksPerWrite_ * blockLength_;
121 delete[] zeroBuffer_;
122 zeroBuffer_ = new char[n];
123 memset(zeroBuffer_, 0, n);
124
125 return 0;
126 }
127
startDao()128 int TaiyoYuden::startDao()
129 {
130 long lba = -leadInLength_ - 150; // Value is not really important since the
131 // LBA is not used by 'writeData'.
132
133 if (writeSession(toc_, multiSession_, 0) != 0) {
134 return 1;
135 }
136
137 log_message(2, "Writing lead-in and gap...");
138
139 // write lead-in
140 if (writeZeros(toc_->leadInMode(), TrackData::SUBCHAN_NONE, lba, 0,
141 leadInLength_) != 0) {
142 flushCache();
143 return 1;
144 }
145
146 // write gap (2 seconds)
147 if (writeZeros(toc_->leadInMode(), TrackData::SUBCHAN_NONE, lba, 0, 150)
148 != 0) {
149 flushCache();
150 return 1;
151 }
152
153
154 log_message(2, "");
155
156 return 0;
157 }
158
finishDao()159 int TaiyoYuden::finishDao()
160 {
161 long lba = toc_->length().lba();
162
163 log_message(2, "Writing lead-out...");
164
165 // write lead-out
166 if (writeZeros(toc_->leadOutMode(), TrackData::SUBCHAN_NONE, lba, lba + 150,
167 leadOutLength_) != 0) {
168 flushCache();
169 return 1;
170 }
171
172 log_message(2, "\nFlushing cache...");
173
174 if (flushCache() != 0) {
175 return 1;
176 }
177
178 log_message(2, "");
179
180 delete[] zeroBuffer_, zeroBuffer_ = NULL;
181
182 return 0;
183 }
184
abortDao()185 void TaiyoYuden::abortDao()
186 {
187 flushCache();
188 }
189
190 // Writes data to target, the block length depends on the actual writing mode
191 // and is stored internally. 'len' is number of blocks to write.
192 // 'lba' specifies the next logical block address for writing and is updated
193 // by this function but not used for writing
194 // return: 0: OK
195 // 1: scsi command failed
writeData(TrackData::Mode mode,TrackData::SubChannelMode sm,long & lba,const char * buf,long len)196 int TaiyoYuden::writeData(TrackData::Mode mode, TrackData::SubChannelMode sm,
197 long &lba, const char *buf, long len)
198 {
199 assert(blocksPerWrite_ > 0);
200 assert(blockLength_ > 0);
201 assert(mode == TrackData::AUDIO);
202 int nwritten = 0;
203 int writeLen = 0;
204 unsigned char cmd[10];
205
206 memset(cmd, 0, 10);
207 cmd[0] = 0x2a; // WRITE1
208
209 while (len > 0) {
210 writeLen = (len > blocksPerWrite_ ? blocksPerWrite_ : len);
211
212 cmd[7] = writeLen >> 8;
213 cmd[8] = writeLen & 0xff;
214
215 if (sendCmd(cmd, 10, (unsigned char *)(buf + (nwritten * blockLength_)),
216 writeLen * blockLength_, NULL, 0) != 0) {
217 log_message(-2, "Write data failed.");
218 return 1;
219 }
220
221 lba += writeLen;
222
223 len -= writeLen;
224 nwritten += writeLen;
225 }
226
227 return 0;
228 }
229
230 // Retrieve disk information.
231 // return: DiskInfo structure or 'NULL' on error
diskInfo()232 DiskInfo *TaiyoYuden::diskInfo()
233 {
234 unsigned char cmd[10];
235 unsigned long dataLen = 34;
236 unsigned char data[34];
237
238 memset(&diskInfo_, 0, sizeof(DiskInfo));
239
240 if (readCapacity(&(diskInfo_.capacity), 0) == 0) {
241 diskInfo_.valid.capacity = 1;
242 }
243
244 if (readSessionInfo(&leadInLength_, &leadOutLength_, 0) == 0) {
245 diskInfo_.valid.manufacturerId = 1;
246 // start time of lead-in
247 diskInfo_.manufacturerId = Msf(450150 - leadInLength_ - 150 );
248 diskInfo_.empty = 1;
249 }
250 else {
251 diskInfo_.empty = 0;
252 }
253
254 diskInfo_.valid.empty = 0; // does not work for this drive
255
256 memset(cmd, 0, 10);
257 memset(data, 0, dataLen);
258
259 cmd[0] = 0x51; // READ DISK INFORMATION
260 cmd[7] = dataLen >> 8;
261 cmd[8] = dataLen;
262
263 if (sendCmd(cmd, 10, NULL, 0, data, dataLen, 0) == 0) {
264 diskInfo_.empty = (data[2] & 0x03) == 0 ? 1 : 0;
265 diskInfo_.cdrw = (data[2] & 0x10) != 0 ? 1 : 0;
266 diskInfo_.valid.cdrw = 1;
267 }
268
269 return &diskInfo_;
270 }
271
272 // Special toc check for Taiyo-Yuden drives. Every track must have a pre-gap
273 // to create a correct disk. The pre-gap length may be only 1 block.
274 // Only warnings are created.
275 // Return: 0: OK
276 // 1: at least one warning was given
277 // 2: at least on error was given
278
checkToc(const Toc * toc)279 int TaiyoYuden::checkToc(const Toc *toc)
280 {
281 int err = PlextorReader::checkToc(toc);
282 int trackNr;
283 Msf start, end;
284 const Track *t;
285 int warningGiven = 0;
286
287 TrackIterator itr(toc);
288
289 for (t = itr.first(start, end), trackNr = 1;
290 t != NULL;
291 t = itr.next(start, end), trackNr++) {
292 if (t->start().lba() == 0 && trackNr > 1) {
293 if (!warningGiven) {
294 warningGiven = 1;
295 log_message(-1, "Each track must have a minimal pre-gap to create a useful disk.");
296 log_message(-1, "This is not fulfilled for following track(s):");
297 }
298 log_message(-1, "Track %d", trackNr);
299 }
300 }
301
302 if (warningGiven) {
303 log_message(0,"");
304 if (err < 1)
305 err = 1;
306 }
307
308 return err;
309 }
310