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