1
/* cdrdao - write audio CD-Rs in disc-at-once mode
3
* Copyright (C) 1998-2002 Andreas Mueller <andreas@daneb.de>
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.
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.
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.
21
#include <sys/types.h>
35
#include "ProcessMonitor.h"
37
#include "guiUpdate.h"
38
#include "ProgressDialog.h"
44
#include "CdrDriver.h"
49
#define DRIVER_ID_DEFAULT 2
51
CdDevice *CdDevice::DEVICE_LIST_ = NULL;
53
char *CdDevice::DRIVER_NAMES_[DRIVER_IDS] = {
70
CdDevice::CdDevice(const char* dev, const char *vendor, const char *product)
76
driverId_ = 0; // undefined
81
manuallyConfigured_ = false;
83
status_ = DEV_UNKNOWN;
87
progressStatusChanged_ = 0;
89
progressTotalTracks_ = 0;
92
progressTrackRelative_ = 0;
93
progressBufferFill_ = 0;
94
progressWriterFill_ = 0;
99
scsiIfInitFailed_ = 0;
107
CdDevice::~CdDevice()
113
char *CdDevice::settingString() const
118
s = "'" + dev_ + "','";
124
switch (deviceType_) {
138
s += driverName(driverId_);
142
sprintf(buf, "0x%lx", options_);
145
return strdupCC(s.c_str());
148
void CdDevice::driverId(int id)
150
if (id >= 0 && id < DRIVER_IDS)
154
void CdDevice::status(Status s)
159
int CdDevice::exitStatus() const
164
int CdDevice::autoSelectDriver()
166
unsigned long options = 0;
167
const char *driverName;
169
driverName = CdrDriver::selectDriver(1, vendor_.c_str(), product_.c_str(),
173
driverId_ = driverName2Id(driverName);
177
bool r_cdr, w_cdr, r_cdrw, w_cdrw;
179
ScsiIf* sif = new ScsiIf(dev_.c_str());
181
if (sif && sif->init() == 0 &&
182
sif->checkMmc(&r_cdr, &w_cdr, &r_cdrw, &w_cdrw)) {
184
driverId_ = driverName2Id("generic-mmc");
185
if (r_cdr) deviceType_ = CD_ROM;
186
if (w_cdr) deviceType_ = CD_R;
187
if (w_cdrw) deviceType_ = CD_RW;
189
driverId_ = DRIVER_ID_DEFAULT;
198
int CdDevice::updateStatus()
200
Status newStatus = status_;
202
if (process_ != NULL) {
203
if (process_->exited()) {
204
newStatus = DEV_UNKNOWN;
205
exitStatus_ = process_->exitStatus();
207
progressStatusChanged_ = 1;
209
PROCESS_MONITOR->remove(process_);
212
if (slaveDevice_ != NULL) {
213
slaveDevice_->status(DEV_UNKNOWN);
219
if (status_ == DEV_READY || status_ == DEV_BUSY || status_ == DEV_NO_DISK ||
220
status_ == DEV_UNKNOWN) {
224
if (scsiIf_ != NULL) {
225
switch (scsiIf_->testUnitReady()) {
227
newStatus = DEV_READY;
230
newStatus = DEV_BUSY;
233
newStatus = DEV_NO_DISK;
236
newStatus = DEV_FAULT;
241
newStatus = DEV_FAULT;
245
if (newStatus != status_) {
253
bool CdDevice::updateProgress(Glib::IOCondition cond, int fd)
255
static char msgSync[4] = { 0xff, 0x00, 0xff, 0x00 };
259
struct timeval timeout = { 0, 0 };
261
if (process_ == NULL)
264
if (!(cond & Gdk::INPUT_READ))
270
while (select(fd + 1, &fds, NULL, NULL, &timeout) > 0 &&
271
FD_ISSET(fd, &fds)) {
278
if (read(fd, buf, 1) != 1) {
279
//message(-2, "Reading of msg sync failed.");
283
if (buf[0] == msgSync[state]) {
289
if (buf[0] == msgSync[state]) {
297
int msgsize = read(fd, (char *)&msg, sizeof(msg));
298
if (msgsize >= PSGMSG_MINSIZE) {
299
if (msg.status >= PGSMSG_MIN && msg.status <= PGSMSG_MAX &&
301
msg.totalProgress >= 0 && msg.totalProgress <= 1000 &&
302
msg.bufferFillRate >= 0 && msg.bufferFillRate <= 100) {
303
progressStatus_ = msg.status;
304
progressTotalTracks_ = msg.totalTracks;
305
progressTrack_ = msg.track;
306
progressTrackRelative_ = msg.trackProgress;
307
progressTotal_ = msg.totalProgress;
308
progressBufferFill_ = msg.bufferFillRate;
309
if (msgsize == sizeof(msg))
310
progressWriterFill_ = msg.writerFillRate;
312
progressWriterFill_ = 0;
314
progressStatusChanged_ = 1;
318
message(-1, _("Reading of progress message failed."));
322
if (progressStatusChanged_)
323
guiUpdate(UPD_PROGRESS_STATUS);
328
CdDevice::DeviceType CdDevice::deviceType() const
333
void CdDevice::deviceType(DeviceType t)
338
unsigned long CdDevice::driverOptions() const
343
void CdDevice::driverOptions(unsigned long o)
348
// Starts a 'cdrdao' for recording given toc. Returns false if an
349
// error occured and the process was not successfully launched.
350
bool CdDevice::recordDao(Gtk::Window& parent, TocEdit *tocEdit, int simulate,
351
int multiSession, int speed, int eject, int reload,
352
int buffer, int overburn)
354
char tocFileName[30];
363
int remoteFdArgNum = 0;
365
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
369
sprintf(tocFileName, "/tmp/gcdm.toc.XXXXXX");
370
int fd = mkstemp(tocFileName);
372
message(-2, _("Cannot create temporary toc-file: %s"), strerror(errno));
376
if (!tocEdit->toc()->write(fd)) {
378
message(-2, _("Cannot write temporary toc-file."));
383
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
384
execName = strdupCC(s);
386
execName = strdupCC("cdrdao");
389
args[n++] = execName;
392
args[n++] = "simulate";
396
args[n++] = "--remote";
404
args[n++] = "--multi";
407
sprintf(speedbuf, "%d", speed);
408
args[n++] = "--speed";
409
args[n++] = speedbuf;
413
args[n++] = "--eject";
416
args[n++] = "--reload";
419
args[n++] = "--overburn";
421
args[n++] = "--device";
422
args[n++] = (char*)dev_.c_str();
425
sprintf(drivername, "%s:0x%lx", driverName(driverId_), options_);
426
args[n++] = "--driver";
427
args[n++] = drivername;
431
sprintf(bufferbuf, "%i", buffer);
432
args[n++] = "--buffers";
433
args[n++] = bufferbuf;
436
args[n++] = tocFileName;
442
PROGRESS_POOL->start(parent, this, tocEdit->filename());
444
// Remove the SCSI interface of this device to avoid problems with double
445
// usage of device nodes.
449
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
452
if (process_ != NULL) {
453
status_ = DEV_RECORDING;
456
if (process_->commFd() >= 0) {
457
Glib::signal_io().connect(bind(slot(*this, &CdDevice::updateProgress),
460
Glib::IO_IN | Glib::IO_HUP);
472
void CdDevice::abortDaoRecording()
474
if (process_ != NULL && !process_->exited()) {
475
PROCESS_MONITOR->stop(process_);
479
int CdDevice::progressStatusChanged()
481
if (progressStatusChanged_) {
482
progressStatusChanged_ = 0;
489
void CdDevice::progress(int *status, int *totalTracks, int *track,
490
int *trackProgress, int *totalProgress,
491
int *bufferFill, int *writerFill) const
493
*status = progressStatus_;
494
*totalTracks = progressTotalTracks_;
495
*track = progressTrack_;
496
*trackProgress = progressTrackRelative_;
497
*totalProgress = progressTotal_;
498
*bufferFill = progressBufferFill_;
499
*writerFill = progressWriterFill_;
502
// Starts a 'cdrdao' for reading whole cd.
503
// Return: 0: OK, process succesfully launched
505
int CdDevice::extractDao(Project& parent, const char *tocFileName,
506
int correction, int readSubChanMode)
514
char correctionbuf[20];
515
int remoteFdArgNum = 0;
517
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
521
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
522
execName = strdupCC(s);
524
execName = strdupCC("cdrdao");
527
args[n++] = execName;
529
args[n++] = "read-cd";
531
args[n++] = "--remote";
538
args[n++] = "--read-raw";
540
switch (readSubChanMode) {
542
args[n++] = "--read-subchan";
547
args[n++] = "--read-subchan";
548
args[n++] = "rw_raw";
552
args[n++] = "--device";
553
args[n++] = (char*)dev_.c_str();
556
sprintf(drivername, "%s:0x%lx", driverName(driverId_), options_);
557
args[n++] = "--driver";
558
args[n++] = drivername;
561
sprintf(correctionbuf, "%d", correction);
562
args[n++] = "--paranoia-mode";
563
args[n++] = correctionbuf;
565
args[n++] = "--datafile";
566
args[n++] = g_strdup_printf("%s.bin", tocFileName);
568
args[n++] = g_strdup_printf("%s.toc", tocFileName);
574
PROGRESS_POOL->start(parent, this, tocFileName, false, false);
576
// Remove the SCSI interface of this device to avoid problems with double
577
// usage of device nodes.
581
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
585
if (process_ != NULL) {
586
status_ = DEV_READING;
589
if (process_->commFd() >= 0) {
590
Glib::signal_io().connect(bind(slot(*this, &CdDevice::updateProgress),
593
Glib::IO_IN | Glib::IO_PRI |
594
Glib::IO_ERR | Glib::IO_HUP);
604
void CdDevice::abortDaoReading()
606
if (process_ != NULL && !process_->exited()) {
607
PROCESS_MONITOR->stop(process_);
611
// Starts a 'cdrdao' for duplicating a CD.
612
// Return: 0: OK, process succesfully launched
614
int CdDevice::duplicateDao(Project& parent, int simulate, int multiSession,
615
int speed, int eject, int reload, int buffer,
616
int onthefly, int correction, int readSubChanMode,
623
char r_drivername[50];
625
char correctionbuf[20];
629
int remoteFdArgNum = 0;
632
int rdstat = readdev->status();
633
if ((rdstat != DEV_READY && rdstat != DEV_UNKNOWN && rdstat != DEV_FAULT) ||
634
readdev->process() != NULL)
637
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
641
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
642
execName = strdupCC(s);
644
execName = strdupCC("cdrdao");
646
args[n++] = execName;
651
args[n++] = "--simulate";
653
args[n++] = "--remote";
661
args[n++] = "--multi";
663
sprintf(correctionbuf, "%d", correction);
664
args[n++] = "--paranoia-mode";
665
args[n++] = correctionbuf;
668
sprintf(speedbuf, "%d", speed);
669
args[n++] = "--speed";
670
args[n++] = speedbuf;
674
args[n++] = "--eject";
677
args[n++] = "--reload";
680
args[n++] = "--on-the-fly";
682
switch (readSubChanMode) {
684
args[n++] = "--read-subchan";
689
args[n++] = "--read-subchan";
690
args[n++] = "rw_raw";
694
args[n++] = "--device";
695
args[n++] = (char*)dev_.c_str();
698
sprintf(drivername, "%s:0x%lx", driverName(driverId_), options_);
699
args[n++] = "--driver";
700
args[n++] = drivername;
704
if (readdev != this) { // reader and write the same, skip source device
706
args[n++] = "--source-device";
707
args[n++] = (char*)readdev->dev();
709
if (readdev->driverId() > 0) {
710
sprintf(r_drivername, "%s:0x%lx", driverName(readdev->driverId()),
711
readdev->driverOptions());
712
args[n++] = "--source-driver";
713
args[n++] = r_drivername;
717
sprintf(bufferbuf, "%i", buffer);
718
args[n++] = "--buffers";
719
args[n++] = bufferbuf;
727
PROGRESS_POOL->start(parent, this, _("CD to CD copy"));
729
// Remove the SCSI interface of this device to avoid problems with double
730
// usage of device nodes.
734
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
738
if (process_ != NULL) {
739
slaveDevice_ = readdev;
740
slaveDevice_->status(DEV_READING);
741
status_ = DEV_RECORDING;
743
action_ = A_DUPLICATE;
745
if (process_->commFd() >= 0) {
746
Glib::signal_io().connect(bind(slot(*this, &CdDevice::updateProgress),
749
Glib::IO_IN | Glib::IO_HUP);
759
void CdDevice::abortDaoDuplication()
761
if (process_ != NULL && !process_->exited()) {
762
PROCESS_MONITOR->stop(process_);
766
// Starts a 'cdrdao' for blanking a CD.
767
// Return: 0: OK, process succesfully launched
769
int CdDevice::blank(Project* parent, int fast, int speed, int eject,
779
int remoteFdArgNum = 0;
781
if ((status_ != DEV_READY && status_ != DEV_FAULT && status_ != DEV_UNKNOWN)
785
if ((s = gnome_config_get_string(SET_CDRDAO_PATH)) != NULL)
786
execName = strdupCC(s);
788
execName = strdupCC("cdrdao");
790
args[n++] = execName;
794
args[n++] = "--remote";
801
args[n++] = "--blank-mode";
804
args[n++] = "minimal";
809
sprintf(speedbuf, "%d", speed);
810
args[n++] = "--speed";
811
args[n++] = speedbuf;
815
args[n++] = "--eject";
818
args[n++] = "--reload";
820
args[n++] = "--device";
821
args[n++] = (char*)dev_.c_str();
824
sprintf(drivername, "%s:0x%lx", driverName(driverId_), options_);
825
args[n++] = "--driver";
826
args[n++] = drivername;
834
PROGRESS_POOL->start(*parent, this, _("Blanking CDRW"), false, false);
836
PROGRESS_POOL->start(this, _("Blanking CDRW"), false, false);
838
// Remove the SCSI interface of this device to avoid problems with double
839
// usage of device nodes.
843
process_ = PROCESS_MONITOR->start(execName, args, remoteFdArgNum);
847
if (process_ != NULL) {
848
status_ = DEV_BLANKING;
851
if (process_->commFd() >= 0) {
852
Glib::signal_io().connect(bind(slot(*this, &CdDevice::updateProgress),
855
Glib::IO_IN | Glib::IO_HUP);
864
void CdDevice::abortBlank()
866
if (process_ != NULL && !process_->exited()) {
867
PROCESS_MONITOR->stop(process_);
871
void CdDevice::createScsiIf()
875
if (scsiIfInitFailed_)
879
scsiIf_ = new ScsiIf(dev_.c_str());
881
if (scsiIf_->init() != 0) {
884
scsiIfInitFailed_ = 1;
888
int CdDevice::driverName2Id(const char *driverName)
892
for (i = 1; i < DRIVER_IDS; i++) {
893
if (strcmp(DRIVER_NAMES_[i], driverName) == 0)
900
int CdDevice::maxDriverId()
902
return DRIVER_IDS - 1;
905
const char *CdDevice::driverName(int id)
907
if (id >= 0 && id < DRIVER_IDS) {
908
return DRIVER_NAMES_[id];
915
const char *CdDevice::status2string(Status s)
942
ret = "Not available";
952
const char *CdDevice::deviceType2string(DeviceType t)
974
/* reads configured devices from gnome settings
976
void CdDevice::importSettings()
983
n = gnome_config_get_int(SET_DEVICES_NUM);
986
gnome_config_push_prefix(SET_SECTION_DEVICES);
988
for (i = 0; i < n; i++) {
989
sprintf(buf, "%d", i);
990
s = gnome_config_get_string(buf);
993
if ((dev = CdDevice::add(s)) != NULL)
994
dev->manuallyConfigured(true);
998
gnome_config_pop_prefix();
1003
/* saves manually configured devices as gnome settings
1005
void CdDevice::exportSettings()
1012
gnome_config_clean_section(SET_SECTION_DEVICES);
1014
for (drun = first(), n = 0; drun != NULL; drun = next(drun)) {
1015
if (drun->manuallyConfigured()) {
1021
gnome_config_set_int(SET_DEVICES_NUM, n);
1023
gnome_config_push_prefix(SET_SECTION_DEVICES);
1025
for (drun = first(), i = 0; drun != NULL; drun = next(drun)) {
1026
if (drun->manuallyConfigured()) {
1027
sprintf(buf, "%d", i);
1028
s = drun->settingString();
1029
gnome_config_set_string(buf, s);
1036
gnome_config_pop_prefix();
1040
CdDevice *CdDevice::add(const char* dev, const char *vendor,
1041
const char *product)
1043
CdDevice *run, *pred, *ent;
1045
for (pred = NULL, run = DEVICE_LIST_; run != NULL;
1046
pred = run, run = run->next_) {
1047
if (strcmp(run->dev(), dev) == 0)
1051
ent = new CdDevice(dev, vendor, product);
1054
ent->next_ = pred->next_;
1058
ent->next_ = DEVICE_LIST_;
1066
static char *nextToken(char *&p)
1070
if (p == NULL || *p == 0)
1073
while (*p != 0 && isspace(*p))
1083
while (*p != 0 && *p != '\'')
1087
// error, no matching ' found
1094
while (*p != 0 && *p != ',')
1104
while (*p != 0 && *p != ',')
1114
static CdDevice *addImpl(char *s)
1122
unsigned long options;
1124
CdDevice::DeviceType type;
1132
if ((val = nextToken(p)) == NULL)
1136
if ((val = nextToken(p)) == NULL)
1140
if ((val = nextToken(p)) == NULL)
1144
if ((val = nextToken(p)) == NULL)
1147
if (strcasecmp(val, "CD_R") == 0)
1148
type = CdDevice::CD_R;
1149
else if (strcasecmp(val, "CD_RW") == 0)
1150
type = CdDevice::CD_RW;
1151
else if (strcasecmp(val, "CD_ROM") == 0)
1152
type = CdDevice::CD_ROM;
1154
type = CdDevice::CD_R;
1156
if ((val = nextToken(p)) == NULL)
1158
driverId = CdDevice::driverName2Id(val);
1160
if ((val = nextToken(p)) == NULL)
1162
options = strtoul(val, NULL, 0);
1164
cddev = CdDevice::add(dev.c_str(), vendor.c_str(), model.c_str());
1166
cddev->driverId(driverId);
1167
cddev->deviceType(type);
1168
cddev->driverOptions(options);
1173
CdDevice *CdDevice::add(const char *setting)
1175
char *s = strdupCC(setting);
1177
CdDevice *dev = addImpl(s);
1186
CdDevice *CdDevice::find(const char* dev)
1190
for (run = DEVICE_LIST_; run != NULL; run = run->next_) {
1191
if (strcmp(run->dev(), dev) == 0)
1198
void CdDevice::scan()
1201
ScsiIf::ScanData *sdata = ScsiIf::scan(&len);
1204
for (i = 0; i < len; i++)
1205
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
1210
sdata = ScsiIf::scan(&len, "ATA");
1212
for (i = 0; i < len; i++)
1213
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
1216
// Only scan for ATAPI devices if we got nothing on the ATA
1217
// interface, otherwise every device would show up twice on the
1219
sdata = ScsiIf::scan(&len, "ATAPI");
1221
for (i = 0; i < len; i++)
1222
CdDevice::add(sdata[i].dev.c_str(), sdata[i].vendor, sdata[i].product);
1229
void CdDevice::remove(const char* dev)
1231
CdDevice *run, *pred;
1233
for (pred = NULL, run = DEVICE_LIST_; run != NULL;
1234
pred = run, run = run->next_) {
1235
if (strcmp(run->dev(), dev) == 0) {
1236
if (run->status() == DEV_RECORDING || run->status() == DEV_BLANKING ||
1237
run->status() == DEV_READING || run->status() == DEV_WAITING)
1241
pred->next_ = run->next_;
1243
DEVICE_LIST_ = run->next_;
1251
void CdDevice::clear()
1255
while (DEVICE_LIST_ != NULL) {
1256
next = DEVICE_LIST_->next_;
1257
delete DEVICE_LIST_;
1258
DEVICE_LIST_ = next;
1262
CdDevice *CdDevice::first()
1264
return DEVICE_LIST_;
1267
CdDevice *CdDevice::next(const CdDevice *run)
1275
int CdDevice::count()
1280
for (run = DEVICE_LIST_; run != NULL; run = run->next_)
1286
int CdDevice::updateDeviceStatus()
1292
blockProcessMonitorSignals();
1294
for (run = DEVICE_LIST_; run != NULL; run = run->next_) {
1295
if (run->updateStatus())
1299
unblockProcessMonitorSignals();