1 /* cdrdao - write audio CD-Rs in disc-at-once mode
2 *
3 * Copyright (C) 1998-2002 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 #include <config.h>
21
22 #include <string.h>
23 #include <assert.h>
24
25 #include "GenericMMCraw.h"
26 #include "PQSubChannel16.h"
27 #include "PWSubChannel96.h"
28 #include "CdTextEncoder.h"
29
30 #include "Toc.h"
31 #include "log.h"
32 #include "port.h"
33
GenericMMCraw(ScsiIf * scsiIf,unsigned long options)34 GenericMMCraw::GenericMMCraw(ScsiIf *scsiIf, unsigned long options)
35 : GenericMMC(scsiIf, options), PQChannelEncoder()
36 {
37 driverName_ = "Generic SCSI-3/MMC (raw writing) - Version 2.0";
38
39 encodingMode_ = 0;
40
41 subChannelMode_ = 0;
42
43 leadInLen_ = leadOutLen_ = 0;
44 subChannel_ = NULL;
45 encSubChannel_ = NULL;
46 encodeBuffer_ = NULL;
47
48 // CD-TEXT dynamic data
49 cdTextStartLba_ = 0;
50 cdTextEndLba_ = 0;
51 cdTextSubChannels_ = NULL;
52 cdTextSubChannelCount_ = 0;
53 cdTextSubChannelAct_ = 0;
54 }
55
~GenericMMCraw()56 GenericMMCraw::~GenericMMCraw()
57 {
58 delete subChannel_, subChannel_ = NULL;
59 delete[] encodeBuffer_, encodeBuffer_ = NULL;
60
61 delete[] encSubChannel_;
62 encSubChannel_ = NULL;
63
64 cdTextStartLba_ = 0;
65 cdTextEndLba_ = 0;
66 cdTextSubChannels_ = NULL;
67 cdTextSubChannelCount_ = 0;
68 cdTextSubChannelAct_ = 0;
69 }
70
multiSession(bool m)71 int GenericMMCraw::multiSession(bool m)
72 {
73 if (m) {
74 // multi session mode is currently not support for raw writing
75 return 1;
76 }
77
78 return 0;
79 }
80
81 // static constructor
instance(ScsiIf * scsiIf,unsigned long options)82 CdrDriver *GenericMMCraw::instance(ScsiIf *scsiIf, unsigned long options)
83 {
84 return new GenericMMCraw(scsiIf, options);
85 }
86
subChannelEncodingMode(TrackData::SubChannelMode sm) const87 int GenericMMCraw::subChannelEncodingMode(TrackData::SubChannelMode sm) const
88 {
89 int ret = 0;
90
91
92 if (subChannelMode_ == 0) {
93 // The supported sub-channel writing mode has not been determined, yet,
94 // so just return the plain mode here. 'initDao' will finally check if
95 // writing of the sub-channel data defined in 'toc_' is supported by the
96 // drive.
97 return 0;
98 }
99
100 switch (sm) {
101 case TrackData::SUBCHAN_NONE:
102 ret = 0;
103 break;
104
105 case TrackData::SUBCHAN_RW:
106 switch (subChannelMode_) {
107 case 2:
108 ret = 0; // plain
109 break;
110 case 3:
111 ret = -1; // currently not supported
112 //ret = 1; // have to create parity and perform interleaving
113 break;
114 default:
115 ret = -1; // not supported
116 break;
117 }
118 break;
119
120 case TrackData::SUBCHAN_RW_RAW:
121 if (subChannelMode_ == 3)
122 ret = 1;
123 else
124 ret = -1;
125 break;
126 }
127
128 return ret;
129 }
130
131 // Sets write parameters via mode page 0x05.
132 // return: 0: OK
133 // 1: scsi command failed
setWriteParameters(int dataBlockType)134 int GenericMMCraw::setWriteParameters(int dataBlockType)
135 {
136 unsigned char mp[0x38];
137
138 if (getModePage(5/*write parameters mode page*/, mp, 0x38,
139 NULL, NULL, 1) != 0) {
140 log_message(-2, "Cannot retrieve write parameters mode page.");
141 return 1;
142 }
143
144 mp[0] &= 0x7f; // clear PS flag
145
146 mp[2] &= 0xe0;
147 mp[2] |= 0x03; // write type: raw
148 if (simulate_) {
149 mp[2] |= 1 << 4; // test write
150 }
151
152 const DriveInfo *di;
153 if ((di = driveInfo(1)) != NULL) {
154 if (di->burnProof) {
155 // This drive has BURN-Proof function.
156 // Enable it unless explicitly disabled.
157 if (bufferUnderRunProtection()) {
158 log_message(2, "Turning BURN-Proof on");
159 mp[2] |= 0x40;
160 }
161 else {
162 log_message(2, "Turning BURN-Proof off");
163 mp[2] &= ~0x40;
164 }
165 }
166
167 RicohSetWriteOptions(di);
168 }
169
170 mp[3] &= 0x3f; // Multi-session: No B0 pointer, next session not allowed
171 mp[3] = 0;
172
173 mp[4] &= 0xf0;
174 mp[4] = dataBlockType & 0x0f; // Data Block Type:
175 // 1: raw data, block size: 2368 PQ sub chan
176 // 2: raw data, block size: 2448
177 // 3: raw data, block size: 2448
178
179 mp[8] = 0; // session format: CD-DA or CD-ROM
180
181 if (setModePage(mp, NULL, NULL, 0) != 0) {
182 //log_message(-2, "Cannot set write parameters mode page.");
183 return 1;
184 }
185
186 return 0;
187 }
188
getMultiSessionInfo(int sessionNr,int multi,SessionInfo * info)189 int GenericMMCraw::getMultiSessionInfo(int sessionNr, int multi,
190 SessionInfo *info)
191 {
192 int err = 0;
193
194 memset(info, 0, sizeof(SessionInfo));
195
196 info->sessionNr = 1;
197
198 if (getSessionInfo() != 0)
199 return 1;
200
201 info->leadInStart = leadInStart_.lba() - 150;
202
203 if (leadInStart_.min() >= 80) {
204 info->leadInStart = leadInStart_.lba() - 450000;
205 }
206
207 info->leadInLen = leadInLen_;
208 info->leadOutLen = leadOutLen_;
209
210 if (multi) {
211 unsigned char cmd[10];
212 unsigned char data[4];
213 unsigned char *buf = NULL;
214 long dataLen;
215
216 // read ATIP data
217 memset(cmd, 0, 10);
218 memset(data, 0, 4);
219
220 cmd[0] = 0x43; // READ TOC/PMA/ATIP
221 cmd[1] = 0x00;
222 cmd[2] = 4; // get ATIP
223 cmd[7] = 0;
224 cmd[8] = 4; // data length
225
226 if (sendCmd(cmd, 10, NULL, 0, data, 4, 0) != 0) {
227 log_message(-2, "Cannot read ATIP data.");
228 return 1;
229 }
230
231 dataLen = (data[0] << 8) | data[1];
232 dataLen += 2;
233
234 log_message(4, "ATIP data len: %ld", dataLen);
235
236 if (sessionNr == 1) {
237 if (dataLen < 19) {
238 log_message(-2, "Cannot read ATIP data.");
239 return 1;
240 }
241 }
242 else {
243 if (dataLen < 15) {
244 log_message(-2, "Cannot read ATIP data.");
245 return 1;
246 }
247 }
248
249 buf = new unsigned char[dataLen];
250 memset(buf, 0, dataLen);
251
252 cmd[7] = dataLen >> 8;
253 cmd[8] = dataLen;
254
255 if (sendCmd(cmd, 10, NULL, 0, buf, dataLen, 0) != 0) {
256 log_message(-2, "Cannot read ATIP data.");
257 delete[] buf;
258 return 1;
259 }
260
261 info->lastLeadoutStart = Msf(buf[12], buf[13], buf[14]);
262
263 if (sessionNr == 1) {
264 info->optimumRecordingPower = buf[4];
265
266 if (buf[8] >= 80 && buf[8] <= 99) {
267 info->atipLeadinStart = Msf(buf[8], buf[9], buf[10]);
268 }
269 else {
270 log_message(-2, "Invalid start time of lead-in in ATIP.");
271 err = 1;
272 }
273
274 info->cdrw = (buf[6] & 0x40) ? 1 : 0;
275
276 if (info->cdrw) {
277 if (buf[6] & 0x04) {
278 info->atipA1[0] = buf[16];
279 info->atipA1[1] = buf[17];
280 info->atipA1[2] = buf[18];
281 }
282 else {
283 log_message(-2, "ATIP data does not contain point A1 data.");
284 err = 1;
285 }
286 }
287 }
288
289 delete[] buf;
290 buf = NULL;
291 }
292
293 log_message(4, "SI: session nr: %d", info->sessionNr);
294 log_message(4, "SI: lead-in start: %ld", info->leadInStart);
295 log_message(4, "SI: lead-in len: %ld", info->leadInLen);
296 log_message(4, "SI: lead-out len: %ld", info->leadOutLen);
297 log_message(4, "SI: last lead-out start: %d %d %d", info->lastLeadoutStart.min(),
298 info->lastLeadoutStart.sec(), info->lastLeadoutStart.frac());
299 log_message(4, "SI: cdrw: %d", info->cdrw);
300 log_message(4, "SI: atip lead-in start: %d %d %d", info->atipLeadinStart.min(),
301 info->atipLeadinStart.sec(), info->atipLeadinStart.frac());
302 log_message(4, "SI: optimum recording power: %u", info->optimumRecordingPower);
303 log_message(4, "SI: atip A1: %u %u %u", info->atipA1[0], info->atipA1[1],
304 info->atipA1[2]);
305
306 return err;
307 }
308
getSubChannelModeFromToc()309 int GenericMMCraw::getSubChannelModeFromToc()
310 {
311 TrackIterator itr(CdrDriver::toc_);
312 const Track *tr;
313 int mode = 0;
314
315 for (tr = itr.first(); tr != NULL; tr = itr.next()) {
316 switch (tr->subChannelType()) {
317 case TrackData::SUBCHAN_NONE:
318 break;
319
320 case TrackData::SUBCHAN_RW:
321 // we need at least packed RW writing
322 if (mode < 2)
323 mode = 2;
324 break;
325
326 case TrackData::SUBCHAN_RW_RAW:
327 // we need raw PW writing
328 mode = 3;
329 break;
330 }
331 }
332
333 log_message(5, "Sub-channel mode requested by toc: %d", mode);
334
335 return mode;
336 }
337
setSubChannelMode()338 int GenericMMCraw::setSubChannelMode()
339 {
340 delete subChannel_;
341 subChannel_ = NULL;
342
343 subChannelMode_ = 0;
344
345 #if 1
346 if (cdTextEncoder_ != NULL) {
347 if (setWriteParameters(3) == 0) {
348 subChannel_ = new PWSubChannel96;
349 subChannelMode_ = 3;
350 }
351 else {
352 delete cdTextEncoder_;
353 cdTextEncoder_ = NULL;
354
355 log_message(force() ? -1 : -2,
356 "Cannot write CD-TEXT data because the 96 byte raw P-W sub-channel data mode is not supported.");
357
358 if (force()) {
359 log_message(-1, "Ignored because of --force option.");
360 }
361 else {
362 log_message(-2, "Use option --force to ignore this error.");
363 return 1;
364 }
365 }
366 }
367
368 // check if the toc requires a certain sub-channel mode and try to set it
369 if (subChannel_ == NULL) {
370 int tocMode = getSubChannelModeFromToc();
371
372 if (tocMode > 0) {
373 for (; tocMode <= 3 && subChannel_ == NULL; tocMode++) {
374 if (setWriteParameters(tocMode) == 0) {
375 if (tocMode == 1)
376 subChannel_ = new PQSubChannel16;
377 else
378 subChannel_ = new PWSubChannel96;
379
380 subChannelMode_ = tocMode;
381 }
382 }
383
384 if (subChannel_ == NULL) {
385 log_message(-2, "Cannot setup sub-channel writing mode for sub-channel data defined in the toc-file.");
386 return 1;
387 }
388 }
389 }
390
391 // select any available sub-channel mode
392 if (subChannel_ == NULL) {
393 if (setWriteParameters(1) == 0) {
394 subChannel_ = new PQSubChannel16;
395 subChannelMode_ = 1;
396 }
397 else if (setWriteParameters(3) == 0) {
398 subChannel_ = new PWSubChannel96;
399 subChannelMode_ = 3;
400 }
401 else if (setWriteParameters(2) == 0) {
402 subChannel_ = new PWSubChannel96;
403 subChannelMode_ = 2;
404 }
405 else {
406 log_message(-2, "Cannot setup disk-at-once writing for this drive.");
407 return 1;
408 }
409 }
410
411 #else
412
413 //subChannel_ = new PWSubChannel96;
414 subChannel_ = new PQSubChannel16;
415 subChannelMode_ = 1;
416 #endif
417
418 switch (subChannelMode_) {
419 case 1:
420 log_message(2, "Using 16 byte P-Q sub-channel data mode.");
421 break;
422 case 2:
423 log_message(2, "Using 96 byte packed P-W sub-channel data mode.");
424 break;
425 case 3:
426 if (cdTextEncoder_ != NULL)
427 log_message(2, "Using 96 byte raw P-W sub-channel data mode for CD-TEXT.");
428 else
429 log_message(2, "Using 96 byte raw P-W sub-channel data mode.");
430 break;
431 }
432
433 return 0;
434 }
435
initDao(const Toc * toc)436 int GenericMMCraw::initDao(const Toc *toc)
437 {
438 long n;
439
440 CdrDriver::toc_ = toc;
441
442 if (selectSpeed() != 0 ||
443 getSessionInfo() != 0) {
444 return 1;
445 }
446
447 delete cdTextEncoder_;
448 cdTextEncoder_ = new CdTextEncoder(toc);
449 if (cdTextEncoder_->encode() != 0) {
450 log_message(-2, "CD-TEXT encoding failed.");
451 return 1;
452 }
453
454 if (cdTextEncoder_->getSubChannels(&n) == NULL || n == 0) {
455 delete cdTextEncoder_;
456 cdTextEncoder_ = NULL;
457 }
458
459 if (setSubChannelMode() != 0)
460 return 1;
461
462 blockLength_ = AUDIO_BLOCK_LEN + subChannel_->dataLength();
463 blocksPerWrite_ = scsiIf_->maxDataLen() / blockLength_;
464 assert(blocksPerWrite_ > 0);
465 log_message(4, "Block length: %ld", blockLength_);
466
467 long cueSheetLen;
468 unsigned char *cueSheet = createCueSheet(0, &cueSheetLen);
469
470 if (cueSheet == NULL) {
471 return 1;
472 }
473
474 if (setCueSheet(subChannel_, sessionFormat(), cueSheet, cueSheetLen,
475 leadInStart_) != 0) {
476 return 1;
477 }
478
479 if (cdTextEncoder_ != NULL) {
480 cdTextStartLba_ = leadInStart_.lba() - 450150;
481 cdTextEndLba_ = cdTextStartLba_ + leadInLen_;
482 cdTextSubChannels_ = cdTextEncoder_->getSubChannels(&cdTextSubChannelCount_);
483 cdTextSubChannelAct_ = 0;
484 }
485 else {
486 cdTextStartLba_ = 0;
487 cdTextEndLba_ = 0;
488 cdTextSubChannels_ = NULL;
489 cdTextSubChannelCount_ = 0;
490 cdTextSubChannelAct_ = 0;
491 }
492
493 // allocate buffer for write zeros
494 n = blocksPerWrite_ * (AUDIO_BLOCK_LEN + subChannel_->dataLength());
495 delete[] zeroBuffer_;
496 zeroBuffer_ = new char[n];
497 memset(zeroBuffer_, 0, n);
498
499 // allocate buffer for sub-channel encoding
500 n = blocksPerWrite_ * blockLength_;
501 delete[] encodeBuffer_;
502 encodeBuffer_ = new unsigned char[n];
503
504 delete[] encSubChannel_;
505 encSubChannel_ = new unsigned char[blocksPerWrite_ * subChannel_->dataLength()];
506
507 /*
508 SessionInfo sessInfo;
509
510 getMultiSessionInfo(1, 1, &sessInfo);
511
512 return 1;
513 */
514
515 if (!simulate_) {
516 if (performPowerCalibration() != 0) {
517 if (!force()) {
518 log_message(-2, "Use option --force to ignore this error.");
519 return 1;
520 }
521 else {
522 log_message(-2, "Ignored because of option --force.");
523 }
524 }
525 }
526
527 return 0;
528 }
529
startDao()530 int GenericMMCraw::startDao()
531 {
532 log_message(2, "Writing lead-in and gap...");
533
534 long lba = leadInStart_.lba() - 450150;
535
536 if (writeZeros(CdrDriver::toc_->leadInMode(), TrackData::SUBCHAN_NONE,
537 lba, 0, leadInLen_) != 0) {
538 return 1;
539 }
540
541 TrackData::SubChannelMode subChanMode = TrackData::SUBCHAN_NONE;
542 TrackIterator itr(CdrDriver::toc_);
543 const Track *tr;
544
545 if ((tr = itr.first()) != NULL) {
546 subChanMode = tr->subChannelType();
547 }
548
549 if (writeZeros(CdrDriver::toc_->leadInMode(), subChanMode, lba, 0, 150)
550 != 0) {
551 return 1;
552 }
553
554 return 0;
555 }
556
finishDao()557 int GenericMMCraw::finishDao()
558 {
559 int ret;
560
561 log_message(2, "Writing lead-out...");
562
563 long lba = CdrDriver::toc_->length().lba();
564
565 writeZeros(CdrDriver::toc_->leadOutMode(), TrackData::SUBCHAN_NONE,
566 lba, lba + 150, leadOutLen_);
567
568 log_message(2, "\nFlushing cache...");
569
570 if (flushCache() != 0) {
571 return 1;
572 }
573
574 while ((ret = checkDriveReady()) == 2)
575 mSleep(2000);
576
577 if (ret != 0)
578 log_message(-1, "TEST UNIT READY failed after recording.");
579
580 delete cdTextEncoder_, cdTextEncoder_ = NULL;
581 delete[] zeroBuffer_, zeroBuffer_ = NULL;
582 delete[] encodeBuffer_, encodeBuffer_ = NULL;
583
584 return 0;
585 }
586
nextWritableAddress()587 long GenericMMCraw::nextWritableAddress()
588 {
589 unsigned char cmd[10];
590 unsigned char data[28];
591 long lba = 0xffffffff;
592
593 memset(cmd, 0, 10);
594 memset(data, 0, 28);
595
596 cmd[0] = 0x52; // READ TRACK INFORMATION
597 cmd[1] = 0;
598 cmd[2] = lba >> 24;
599 cmd[3] = lba >> 16;
600 cmd[4] = lba >> 8;
601 cmd[5] = lba;
602 cmd[8] = 28;
603
604 if (sendCmd(cmd, 10, NULL, 0, data, 28) != 0) {
605 log_message(-2, "Cannt get track information.");
606 return 0;
607 }
608
609 long adr = (data[12] << 24) | (data[13] << 16) | (data[14] << 8) |
610 data[15];
611
612 return adr;
613
614 }
615
616 // Writes data to target. The encoded sub-channel data is appended to each
617 // block.
618 // return: 0: OK
619 // 1: scsi command failed
writeData(TrackData::Mode mode,TrackData::SubChannelMode sm,long & lba,const char * buf,long len)620 int GenericMMCraw::writeData(TrackData::Mode mode,
621 TrackData::SubChannelMode sm,
622 long &lba, const char *buf, long len)
623 {
624 assert(blockLength_ > 0);
625 assert(blocksPerWrite_ > 0);
626 assert(mode == TrackData::AUDIO);
627 int writeLen = 0;
628 unsigned char cmd[10];
629 int i, j;
630
631 long iblen = blockSize(mode, sm);
632 long slen = subChannel_->dataLength();
633
634 /*
635 log_message(0, "lba: %ld, len: %ld, bpc: %d, bl: %d ", lba, len, blocksPerCmd,
636 blockLength_);
637 */
638
639 memset(cmd, 0, 10);
640 cmd[0] = 0x2a; // WRITE1
641
642 while (len > 0) {
643 writeLen = (len > blocksPerWrite_ ? blocksPerWrite_ : len);
644
645 cmd[2] = lba >> 24;
646 cmd[3] = lba >> 16;
647 cmd[4] = lba >> 8;
648 cmd[5] = lba;
649
650 cmd[7] = writeLen >> 8;
651 cmd[8] = writeLen;
652
653 // encode the PQ sub-channel data
654 encode(lba, encSubChannel_, writeLen);
655
656 for (i = 0; i < writeLen; i++) {
657 memcpy(encodeBuffer_ + i * blockLength_, buf + i * iblen,
658 AUDIO_BLOCK_LEN);
659
660 memcpy(encodeBuffer_ + i * blockLength_ + AUDIO_BLOCK_LEN,
661 encSubChannel_ + i * slen, slen);
662
663 if (cdTextSubChannels_ != NULL && lba >= cdTextStartLba_ &&
664 lba + i < cdTextEndLba_) {
665
666 const unsigned char *data = cdTextSubChannels_[cdTextSubChannelAct_]->data();
667 long dataLen = cdTextSubChannels_[cdTextSubChannelAct_]->dataLength();
668
669 unsigned char *actBuf = encodeBuffer_ + i * blockLength_ + AUDIO_BLOCK_LEN;
670
671 //log_message(0, "Adding CD-TEXT channel %ld for LBA %ld", cdTextSubChannelAct_, lba + i);
672 for (j = 0; j < dataLen; j++) {
673 *actBuf |= (*data & 0x3f);
674 actBuf++;
675 data++;
676 }
677
678 cdTextSubChannelAct_++;
679 if (cdTextSubChannelAct_ >= cdTextSubChannelCount_)
680 cdTextSubChannelAct_ = 0;
681 }
682 else {
683 switch (sm) {
684 case TrackData::SUBCHAN_NONE:
685 break;
686
687 case TrackData::SUBCHAN_RW:
688 case TrackData::SUBCHAN_RW_RAW:
689 {
690 unsigned char *oBuf = encodeBuffer_ + i * blockLength_ + AUDIO_BLOCK_LEN;
691 const char *iBuf = buf + i * iblen + AUDIO_BLOCK_LEN;
692
693 for (j = 0; j < PW_SUBCHANNEL_LEN; j++) {
694 *oBuf |= (*iBuf & 0x3f);
695 oBuf++;
696 iBuf++;
697 }
698 }
699 break;
700 }
701 }
702 }
703
704
705 #if 0
706 // consistency checks
707 long sum1, sum2;
708 int n;
709 const char *p;
710 for (i = 0; i < writeLen; i++) {
711 log_message(0, "%ld: ", lba + i);
712 SubChannel *chan = subChannel_->makeSubChannel(encodeBuffer_ + i * blockLength_ + AUDIO_BLOCK_LEN);
713 chan->print();
714 delete chan;
715
716 sum1 = 0;
717 for (p = buf + i * iblen, n = 0;
718 n < AUDIO_BLOCK_LEN; n++, p++) {
719 sum1 += *p;
720 }
721
722 sum2 = 0;
723 for (n = 0; n < AUDIO_BLOCK_LEN; n++) {
724 sum2 += *(char *)(encodeBuffer_ + i * blockLength_ + n);
725 }
726
727 //log_message(0, "%ld - %ld", sum1, sum2);
728 assert(sum1 == sum2);
729 }
730 #endif
731
732 #if 1
733 if (sendCmd(cmd, 10, encodeBuffer_, writeLen * blockLength_,
734 NULL, 0) != 0) {
735 log_message(-2, "Write data failed.");
736 return 1;
737 }
738 #endif
739 //log_message(0, ". ");
740
741 lba += writeLen;
742 len -= writeLen;
743 buf += writeLen * iblen;
744 }
745
746 //log_message(0, "");
747
748 return 0;
749 }
750