summaryrefslogtreecommitdiff
path: root/wodim
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
committerIgor Pashev <pashev.igor@gmail.com>2012-12-31 05:04:42 +0400
commit71dc8760ff4de5f365330d1bc571d934deb54af9 (patch)
tree7346d42a282562a3937d82307012b5857d642ce6 /wodim
downloadcdrkit-71dc8760ff4de5f365330d1bc571d934deb54af9.tar.gz
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'wodim')
-rw-r--r--wodim/CMakeLists.txt37
-rw-r--r--wodim/audiosize.c289
-rw-r--r--wodim/auheader.h40
-rw-r--r--wodim/auinfo.c525
-rw-r--r--wodim/cd_misc.c144
-rw-r--r--wodim/cdr_drv.c318
-rw-r--r--wodim/cdtext.c546
-rw-r--r--wodim/cdtext.h143
-rw-r--r--wodim/clone.c270
-rw-r--r--wodim/crc16.c160
-rw-r--r--wodim/crc16.h41
-rw-r--r--wodim/cue.c1198
-rw-r--r--wodim/defaults.c139
-rw-r--r--wodim/defaults.h14
-rw-r--r--wodim/diskid.c522
-rw-r--r--wodim/drv_7501.c1030
-rw-r--r--wodim/drv_jvc.c1433
-rw-r--r--wodim/drv_mmc.c4362
-rw-r--r--wodim/drv_philips.c1346
-rw-r--r--wodim/drv_simul.c393
-rw-r--r--wodim/drv_sony.c1364
-rw-r--r--wodim/fifo.c878
-rw-r--r--wodim/getnum.c123
-rw-r--r--wodim/iso9660.h180
-rw-r--r--wodim/isosize.c86
-rw-r--r--wodim/misc.c82
-rw-r--r--wodim/mmcvendor.h87
-rw-r--r--wodim/modes.c289
-rw-r--r--wodim/movesect.c104
-rw-r--r--wodim/movesect.h45
-rw-r--r--wodim/scsi_cdr.c2918
-rw-r--r--wodim/scsi_cdr_mmc4.c80
-rw-r--r--wodim/scsi_mmc.c411
-rw-r--r--wodim/scsi_mmc4.c83
-rw-r--r--wodim/scsi_scan.c379
-rw-r--r--wodim/scsi_scan.h44
-rw-r--r--wodim/scsimmc.h593
-rw-r--r--wodim/sector.c263
-rw-r--r--wodim/subchan.c997
-rw-r--r--wodim/wm_packet.c306
-rw-r--r--wodim/wm_session.c51
-rw-r--r--wodim/wm_track.c50
-rw-r--r--wodim/wodim.12369
-rw-r--r--wodim/wodim.c4701
-rw-r--r--wodim/wodim.dfl48
-rw-r--r--wodim/wodim.h1212
-rw-r--r--wodim/xio.c164
-rw-r--r--wodim/xio.h54
48 files changed, 30911 insertions, 0 deletions
diff --git a/wodim/CMakeLists.txt b/wodim/CMakeLists.txt
new file mode 100644
index 0000000..d6245c9
--- /dev/null
+++ b/wodim/CMakeLists.txt
@@ -0,0 +1,37 @@
+PROJECT (CDRECORD C)
+INCLUDE_DIRECTORIES(../include ../libedc ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include)
+INCLUDE(../include/AddScgBits.cmake)
+include(../include/AddSchilyBits.cmake)
+
+#AUX_SOURCE_DIRECTORY(. CDRECORD_SRCS)
+SET(CDRECORD_SRCS wodim.c audiosize.c auinfo.c cdr_drv.c cdtext.c clone.c crc16.c cue.c diskid.c drv_7501.c drv_jvc.c drv_mmc.c drv_philips.c drv_simul.c drv_sony.c fifo.c isosize.c scsi_cdr_mmc4.c scsi_mmc4.c sector.c subchan.c wm_packet.c wm_session.c wm_track.c xio.c)
+SET(CDRECORD_COMMON_SRCS cd_misc.c defaults.c getnum.c misc.c modes.c movesect.c scsi_cdr.c scsi_mmc.c scsi_scan.c)
+
+INCLUDE(CheckIncludeFiles)
+
+#force libcap usage on Linux
+CHECK_INCLUDE_FILES("sys/capability.h" HAVE_SYS_CAPABILITY_H)
+IF(HAVE_SYS_CAPABILITY_H)
+ LIST(APPEND EXTRA_LIBS cap)
+ELSE(HAVE_SYS_CAPABILITY_H)
+ IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ MESSAGE(FATAL_ERROR "Error: found a Linux system but no libcap header. Install libcap-dev.")
+ ENDIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
+ENDIF(HAVE_SYS_CAPABILITY_H)
+
+LINK_DIRECTORIES(../librols ../libusal ../libedc)
+
+ADD_DEFINITIONS(-DHAVE_LIB_EDC_ECC -DCLONE_WRITE -DDRV_DVD -DFIFO -DAUINFO -DUSE_LARGEFILES )
+
+
+ADD_EXECUTABLE (wodim ${CDRECORD_SRCS})
+ADD_LIBRARY (wodimstuff STATIC ${CDRECORD_COMMON_SRCS})
+LIST(APPEND EXTRA_LIBS wodimstuff)
+
+TARGET_LINK_LIBRARIES(wodim ${EXTRA_LIBS} edc)
+SET_TARGET_PROPERTIES(wodim PROPERTIES SKIP_BUILD_RPATH TRUE)
+
+INSTALL(TARGETS wodim DESTINATION bin)
+INSTALL(FILES
+ wodim.1
+DESTINATION ${MANSUBDIR}/man1)
diff --git a/wodim/audiosize.c b/wodim/audiosize.c
new file mode 100644
index 0000000..2cdad97
--- /dev/null
+++ b/wodim/audiosize.c
@@ -0,0 +1,289 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)audiosize.c 1.19 04/03/01 Copyright 1998-2004 J. Schilling */
+/*
+ * Copyright (c) 1998-2004 J. Schilling
+ *
+ * First .vaw implementation made by Dave Platt <dplatt@iq.nc.com>
+ * Current .wav implementation with additional help from Heiko Eißfeld.
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <statdefs.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include "auheader.h"
+
+typedef struct {
+ Uchar magic[4];
+ Uchar hdr_size[4];
+ Uchar data_size[4];
+ Uchar encoding[4];
+ Uchar sample_rate[4];
+ Uchar channels[4];
+} sun_au_t;
+
+#define SUN_AU_MAGIC ".snd"
+#define SUN_AU_UNKNOWN_LEN ((Uint)~0)
+#define SUN_AU_ULAW8 1 /* American ISDN Telephonie */
+#define SUN_AU_LINEAR8 2 /* Linear PCM 8 bit/channel */
+#define SUN_AU_LINEAR16 3 /* Linear PCM 16 bit/channel */
+#define SUN_AU_LINEAR24 4 /* Linear PCM 24 bit/channel */
+#define SUN_AU_LINEAR32 5 /* Linear PCM 32 bit/channel */
+#define SUN_AU_FLOAT 6 /* 32 bit IEEE floatingpoint */
+#define SUN_AU_DOUBLE 7 /* 64 bit IEEE floatingpoint */
+#define SUN_AU_G721 23 /* 4 bit CCITT G.721 ADPCM */
+#define SUN_AU_G722 24 /* CCITT G.722 ADPCM */
+#define SUN_AU_G723_3 25 /* 3 bit CCITT G.723 ADPCM */
+#define SUN_AU_G723_5 26 /* 5 bit CCITT G.723 ADPCM */
+#define SUN_AU_ALAW8 27 /* International ISDN Tel. */
+
+typedef struct {
+ Uchar ckid[4];
+ Uchar cksize[4];
+} chunk_t;
+
+typedef struct {
+ Uchar wave[4];
+} riff_chunk;
+
+typedef struct {
+ Uchar fmt_tag[2];
+ Uchar channels[2];
+ Uchar sample_rate[4];
+ Uchar av_byte_rate[4];
+ Uchar block_size[2];
+ Uchar bits_per_sample[2];
+} fmt_chunk;
+
+#define WAV_RIFF_MAGIC "RIFF" /* Magic for file format */
+#define WAV_WAVE_MAGIC "WAVE" /* Magic for Waveform Audio */
+#define WAV_FMT_MAGIC "fmt " /* Start of Waveform format */
+#define WAV_DATA_MAGIC "data" /* Start of data chunk */
+#define WAV_FORMAT_PCM 0x0001 /* Linear PCM format */
+#define WAV_FORMAT_ULAW 0x0101 /* American ISDN Telephonie */
+#define WAV_FORMAT_ALAW 0x0102 /* International ISDN Tel. */
+#define WAV_FORMAT_ADPCM 0x0103 /* ADPCM format */
+
+#define le_a_to_u_short(a) ((unsigned short) \
+ ((((unsigned char *)a)[0] & 0xFF) | \
+ (((unsigned char *)a)[1] << 8 & 0xFF00)))
+
+#ifdef __STDC__
+#define le_a_to_u_long(a) ((unsigned long) \
+ ((((unsigned char *)a)[0] & 0xFF) | \
+ (((unsigned char *)a)[1] << 8 & 0xFF00) | \
+ (((unsigned char *)a)[2] << 16 & 0xFF0000) | \
+ (((unsigned char *)a)[3] << 24 & 0xFF000000UL)))
+#else
+#define le_a_to_u_long(a) ((unsigned long) \
+ ((((unsigned char *)a)[0] & 0xFF) | \
+ (((unsigned char *)a)[1] << 8 & 0xFF00) | \
+ (((unsigned char *)a)[2] << 16 & 0xFF0000) | \
+ (((unsigned char *)a)[3] << 24 & 0xFF000000)))
+#endif
+
+BOOL is_auname(const char *name);
+off_t ausize(int f);
+BOOL is_wavname(const char *name);
+off_t wavsize(int f);
+
+BOOL
+is_auname(const char *name)
+{
+ const char *p;
+
+ if ((p = strrchr(name, '.')) == NULL)
+ return (FALSE);
+ return (streql(p, ".au"));
+}
+
+/*
+ * Read Sun audio header, leave file seek pointer past auheader.
+ */
+off_t
+ausize(int f)
+{
+ sun_au_t hdr;
+ struct stat sb;
+ mode_t mode;
+ off_t size;
+ Int32_t val;
+ long ret = AU_BAD_HEADER;
+
+ /*
+ * First check if a bad guy tries to call ausize()
+ * with an unappropriate file descriptor.
+ * return -1 in this case.
+ */
+ if (isatty(f))
+ return (-1L);
+ if (fstat(f, &sb) < 0)
+ return (-1L);
+ mode = sb.st_mode & S_IFMT;
+ if (!S_ISREG(mode) && !S_ISBLK(mode) && !S_ISCHR(mode))
+ return (-1L);
+
+ if (read(f, &hdr, sizeof (hdr)) != sizeof (hdr))
+ goto err;
+
+ if (strncmp((char *)hdr.magic, SUN_AU_MAGIC, 4) != 0)
+ goto err;
+
+ ret = AU_BAD_CODING;
+
+ val = a_to_u_4_byte(hdr.encoding);
+ if (val != SUN_AU_LINEAR16)
+ goto err;
+
+ val = a_to_u_4_byte(hdr.channels);
+ if (val != 2)
+ goto err;
+
+ val = a_to_u_4_byte(hdr.sample_rate);
+ if (val != 44100)
+ goto err;
+
+ size = (off_t)a_to_u_4_byte(hdr.hdr_size);
+ if (size < (off_t)sizeof (hdr) || size > 512)
+ goto err;
+ lseek(f, size, SEEK_SET);
+
+ /*
+ * Most .au files don't seem to honor the data_size field,
+ * so we use the whole file size without the header.
+ */
+ size = sb.st_size - size;
+ return (size);
+
+err:
+ lseek(f, (off_t)0L, SEEK_SET);
+ return ((off_t)ret);
+}
+
+BOOL
+is_wavname(const char *name)
+{
+ const char *p;
+
+ if ((p = strrchr(name, '.')) == NULL)
+ return (FALSE);
+ return (streql(p, ".wav") || streql(p, ".WAV"));
+}
+
+/*
+ * Read WAV header, leave file seek pointer past WAV header.
+ */
+off_t
+wavsize(int f)
+{
+ chunk_t chunk;
+ riff_chunk riff;
+ fmt_chunk fmt;
+ struct stat sb;
+ off_t cursor;
+ BOOL gotFormat;
+ mode_t mode;
+ off_t size;
+ long ret = AU_BAD_HEADER;
+
+ /*
+ * First check if a bad guy tries to call wavsize()
+ * with an unappropriate file descriptor.
+ * return -1 in this case.
+ */
+
+ if (isatty(f))
+ return (-1L);
+ if (fstat(f, &sb) < 0)
+ return (-1L);
+ mode = sb.st_mode & S_IFMT;
+ if (!S_ISREG(mode) && !S_ISBLK(mode) && !S_ISCHR(mode))
+ return (-1L);
+
+ cursor = (off_t)0;
+ gotFormat = FALSE;
+
+ for (;;) {
+ if (read(f, &chunk, sizeof (chunk)) != sizeof (chunk))
+ goto err;
+ size = (off_t)le_a_to_u_long(chunk.cksize);
+
+ if (strncmp((char *)chunk.ckid, WAV_RIFF_MAGIC, 4) == 0) {
+ /*
+ * We found (first) RIFF header. Check if a WAVE
+ * magic follows. Set up size to be able to skip
+ * past this header.
+ */
+ if (read(f, &riff, sizeof (riff)) != sizeof (riff))
+ goto err;
+ if (strncmp((char *)riff.wave, WAV_WAVE_MAGIC, 4) != 0)
+ goto err;
+ size = (off_t)sizeof (riff);
+
+ } else if (strncmp((char *)chunk.ckid, WAV_FMT_MAGIC, 4) == 0) {
+ /*
+ * We found WAVE "fmt " header. Check size (if it is
+ * valid for a WAVE file) and coding whether it is
+ * useable for a CD.
+ */
+ if (size < (off_t)sizeof (fmt)) goto err;
+ if (sizeof (fmt) != read(f, &fmt, sizeof (fmt))) goto err;
+ if (le_a_to_u_short(fmt.channels) != 2 ||
+ le_a_to_u_long(fmt.sample_rate) != 44100 ||
+ le_a_to_u_short(fmt.bits_per_sample) != 16) {
+ ret = AU_BAD_CODING;
+ goto err;
+ }
+ gotFormat = TRUE;
+
+ } else if (strncmp((char *)chunk.ckid, WAV_DATA_MAGIC, 4) == 0) {
+ /*
+ * We found WAVE "data" header. This contains the
+ * size value of the audio part.
+ */
+ if (!gotFormat) {
+ ret = AU_BAD_CODING;
+ goto err;
+ }
+ if ((cursor + size + sizeof (chunk)) > sb.st_size)
+ size = sb.st_size - (cursor + sizeof (chunk));
+ return (size);
+ }
+ cursor += size + sizeof (chunk);
+ lseek(f, cursor, SEEK_SET); /* Skip over current chunk */
+ }
+err:
+ lseek(f, (off_t)0L, SEEK_SET);
+ return (ret);
+}
diff --git a/wodim/auheader.h b/wodim/auheader.h
new file mode 100644
index 0000000..3bfe932
--- /dev/null
+++ b/wodim/auheader.h
@@ -0,0 +1,40 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)auheader.h 1.2 98/05/09 Copyright 1998 J. Schilling */
+/*
+ * Copyright (c) 1998 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _AUHEADER_H
+#define _AUHEADER_H
+
+#define AU_BAD -1
+#define AU_BAD_HEADER -2
+#define AU_BAD_CODING -3
+
+
+#endif /* _AUHEADER_H */
diff --git a/wodim/auinfo.c b/wodim/auinfo.c
new file mode 100644
index 0000000..8cf8aef
--- /dev/null
+++ b/wodim/auinfo.c
@@ -0,0 +1,525 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)auinfo.c 1.23 04/03/01 Copyright 1998-2004 J. Schilling */
+/*
+ * Copyright (c) 1998-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <statdefs.h>
+#include <stdio.h>
+#include <standard.h>
+#include <strdefs.h>
+#include <deflts.h>
+#include <utypes.h>
+#include <schily.h>
+
+#include "cdtext.h"
+#include "wodim.h"
+
+extern int debug;
+extern int xdebug;
+
+BOOL auinfosize(char *name, track_t *trackp);
+void auinfo(char *name, int track, track_t *trackp);
+textptr_t *gettextptr(int track, track_t *trackp);
+static char *savestr(char *name);
+static char *readtag(char *name);
+static char *readtstr(char *name);
+void setmcn(char *mcn, track_t *trackp);
+static void isrc_illchar(char *isrc, int c);
+void setisrc(char *isrc, track_t *trackp);
+void setindex(char *tindex, track_t *trackp);
+
+#ifdef XXX
+int
+main(int argc, char *argv[])
+{
+/* auinfo("/etc/default/cdrecord");*/
+/* auinfo("/mnt2/CD3/audio_01.inf");*/
+ auinfo("/mnt2/CD3/audio_01.wav");
+}
+#endif
+
+BOOL
+auinfosize(char *name, track_t *trackp)
+{
+ const char *p;
+ const char *tlp;
+ struct stat sb;
+ long secs;
+ long nsamples;
+ Llong tracksize;
+
+ if (!is_audio(trackp))
+ return (FALSE);
+
+ if ((trackp->flags & TI_USEINFO) == 0)
+ return (FALSE);
+
+ if ((p = strrchr(name, '.')) == NULL)
+ return (FALSE);
+ if (!streql(p, ".inf") && !streql(p, ".INF"))
+ return (FALSE);
+
+ /*
+ * First check if a bad guy tries to call auinfosize()
+ * while STDIN_FILENO is a TTY.
+ */
+ if (isatty(STDIN_FILENO)) {
+ errmsgno(EX_BAD,
+ "WARNING: Stdin is connected to a terminal.\n");
+ return (FALSE);
+ }
+
+ if (stat(name, &sb) < 0) /* *.inf file not found */
+ return (FALSE);
+
+ if (sb.st_size > 10000) /* Too large for a *.inf file */
+ return (FALSE);
+
+ if (cfg_open(name) < 0) /* Cannot open *.inf file */
+ return (FALSE);
+
+ tlp = p = readtag("Tracklength");
+ if (p == NULL) { /* Tracklength= Tag not found */
+ errmsgno(EX_BAD,
+ "WARNING: %s does not contain a 'Tracklength=' tag.\n",
+ name);
+ cfg_close();
+ return (FALSE);
+ }
+
+ p = astol(p, &secs);
+ if (*p != '\0' && *p != ',') {
+ errmsgno(EX_BAD,
+ "WARNING: %s: 'Tracklength=' contains illegal parameter '%s'.\n",
+ name, tlp);
+ cfg_close();
+ return (FALSE);
+ }
+ if (*p == ',')
+ p++;
+ p = astol(p, &nsamples);
+ if (*p != '\0') {
+ errmsgno(EX_BAD,
+ "WARNING: %s: 'Tracklength=' contains illegal parameter '%s'.\n",
+ name, tlp);
+ cfg_close();
+ return (FALSE);
+ }
+ tracksize = (secs * 2352) + (nsamples * 4);
+ if (xdebug > 0) {
+ fprintf(stderr, "%s: Tracksize %lld bytes (%ld sectors, %ld samples)\n",
+ name, tracksize, secs, nsamples);
+ }
+ trackp->itracksize = tracksize;
+ cfg_close();
+ return (TRUE);
+}
+
+void
+auinfo(char *name, int track, track_t *trackp)
+{
+ char infname[1024];
+ char *p;
+ track_t *tp = &trackp[track];
+ textptr_t *txp;
+ long l;
+ long tno = -1;
+ BOOL isdao = !is_tao(&trackp[0]);
+
+ strncpy(infname, name, sizeof (infname)-1);
+ infname[sizeof (infname)-1] = '\0';
+ p = strrchr(infname, '.');
+ if (p != 0 && &p[4] < &name[sizeof (infname)]) {
+ strcpy(&p[1], "inf");
+ }
+
+ if (cfg_open(infname) == 0) {
+
+ p = readtstr("CDINDEX_DISCID");
+ p = readtag("CDDB_DISKID");
+
+ p = readtag("MCN");
+ if (p && *p) {
+ setmcn(p, &trackp[0]);
+ txp = gettextptr(0, trackp); /* MCN is isrc for trk 0*/
+ txp->tc_isrc = savestr(p);
+ }
+
+ p = readtag("ISRC");
+ if (p && *p) {
+ setisrc(p, &trackp[track]);
+ txp = gettextptr(track, trackp);
+ txp->tc_isrc = savestr(p);
+ }
+
+ p = readtstr("Albumperformer");
+ if (p && *p) {
+ txp = gettextptr(0, trackp); /* Album perf. in trk 0*/
+ txp->tc_performer = savestr(p);
+ }
+ p = readtstr("Performer");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_performer = savestr(p);
+ }
+ p = readtstr("Albumtitle");
+ if (p && *p) {
+ txp = gettextptr(0, trackp); /* Album title in trk 0*/
+ txp->tc_title = savestr(p);
+ }
+ p = readtstr("Tracktitle");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_title = savestr(p);
+ }
+ p = readtstr("Songwriter");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_songwriter = savestr(p);
+ }
+ p = readtstr("Composer");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_composer = savestr(p);
+ }
+ p = readtstr("Arranger");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_arranger = savestr(p);
+ }
+ p = readtstr("Message");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_message = savestr(p);
+ }
+ p = readtstr("Diskid");
+ if (p && *p) {
+ txp = gettextptr(0, trackp); /* Disk id is in trk 0*/
+ txp->tc_title = savestr(p);
+ }
+ p = readtstr("Closed_info");
+ if (p && *p) {
+ txp = gettextptr(track, trackp);
+ txp->tc_closed_info = savestr(p);
+ }
+
+ p = readtag("Tracknumber");
+ if (p && isdao)
+ astol(p, &tno);
+
+ p = readtag("Trackstart");
+ if (p && isdao) {
+ l = -1L;
+ astol(p, &l);
+ if (track == 1 && tno == 1 && l > 0) {
+ trackp[1].pregapsize = 150 + l;
+ printf("Track1 Start: '%s' (%ld)\n", p, l);
+ }
+ }
+
+ p = readtag("Tracklength");
+
+ p = readtag("Pre-emphasis");
+ if (p && *p) {
+ if (strncmp(p, "yes", 3) == 0) {
+ tp->flags |= TI_PREEMP;
+ if ((tp->tracktype & TOC_MASK) == TOC_DA)
+ tp->sectype = SECT_AUDIO_PRE;
+
+ } else if (strncmp(p, "no", 2) == 0) {
+ tp->flags &= ~TI_PREEMP;
+ if ((tp->tracktype & TOC_MASK) == TOC_DA)
+ tp->sectype = SECT_AUDIO_NOPRE;
+ }
+ }
+
+ p = readtag("Channels");
+ p = readtag("Copy_permitted");
+ if (p && *p) {
+ /*
+ * -useinfo always wins
+ */
+ tp->flags &= ~(TI_COPY|TI_SCMS);
+
+ if (strncmp(p, "yes", 3) == 0)
+ tp->flags |= TI_COPY;
+ else if (strncmp(p, "no", 2) == 0)
+ tp->flags |= TI_SCMS;
+ else if (strncmp(p, "once", 2) == 0)
+ tp->flags &= ~(TI_COPY|TI_SCMS);
+ }
+ p = readtag("Endianess");
+ p = readtag("Index");
+ if (p && *p && isdao)
+ setindex(p, &trackp[track]);
+
+ p = readtag("Index0");
+ if (p && isdao) {
+ Llong ts;
+ Llong ps;
+
+ l = -2L;
+ astol(p, &l);
+ if (l == -1) {
+ trackp[track+1].pregapsize = 0;
+ } else if (l > 0) {
+ ts = tp->itracksize / tp->isecsize;
+ ps = ts - l;
+ if (ps > 0)
+ trackp[track+1].pregapsize = ps;
+ }
+ }
+ }
+
+}
+
+textptr_t *
+gettextptr(int track, track_t *trackp)
+{
+ register textptr_t *txp;
+
+ txp = (textptr_t *)trackp[track].text;
+ if (txp == NULL) {
+ txp = malloc(sizeof (textptr_t));
+ if (txp == NULL)
+ comerr("Cannot malloc CD-Text structure.\n");
+ fillbytes(txp, sizeof (textptr_t), '\0');
+ trackp[track].text = txp;
+ }
+ return (txp);
+}
+
+static char *
+savestr(char *str)
+{
+ char *ret;
+
+ ret = malloc(strlen(str)+1);
+ if (ret)
+ strcpy(ret, str);
+ else
+ comerr("Cannot malloc auinfo string.\n");
+ return (ret);
+}
+
+static char *
+readtag(char *name)
+{
+ register char *p;
+
+ p = cfg_get(name);
+ if (debug)
+ printf("%s '%s'\n", name, p);
+ return (p);
+}
+
+static char *
+readtstr(char *name)
+{
+ register char *p;
+ register char *p2;
+
+ p = readtag(name);
+ if (p && *p == '\'') {
+ p2 = ++p;
+ while (*p2 != '\0')
+ p2++;
+ while (p2 > p && *p2 != '\'')
+ p2--;
+ *p2 = '\0';
+ if (debug)
+ printf("%s '%s'\n", name, p);
+ }
+ return (p);
+}
+
+/*
+ * Media catalog number is a 13 digit number.
+ */
+void
+setmcn(char *mcn, track_t *trackp)
+{
+ register char *p;
+
+ if (strlen(mcn) != 13)
+ comerrno(EX_BAD, "MCN '%s' has illegal length.\n", mcn);
+
+ for (p = mcn; *p; p++) {
+ if (*p < '0' || *p > '9')
+ comerrno(EX_BAD, "MCN '%s' contains illegal character '%c'.\n", mcn, *p);
+ }
+ p = malloc(14);
+ strcpy(p, mcn);
+ trackp->isrc = p;
+
+ if (debug)
+ printf("Track %d MCN: '%s'\n", (int)trackp->trackno, trackp->isrc);
+}
+
+static char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+static void
+isrc_illchar(char *isrc, int c)
+{
+ errmsgno(EX_BAD, "ISRC '%s' contains illegal character '%c'.\n", isrc, c);
+}
+
+/*
+ * ISRC is 12 Byte:
+ *
+ * Country code 'C' (alpha) 2 Bytes
+ * Owner code 'O' (alphanumeric) 3 Bytes
+ * Year of record 'Y' (numeric) 2 Bytes
+ * Serial number 'S' (numeric) 5 Bytes
+ *
+ * CC-OOO-YY-SSSSS
+ */
+void
+setisrc(char *isrc, track_t *trackp)
+{
+ char ibuf[13];
+ char *ip;
+ char *p;
+ int i;
+ int len;
+
+ if ((len = strlen(isrc)) != 12) {
+ for (p = isrc, i = 0; *p; p++) {
+ if (*p == '-')
+ i++;
+ }
+ if (((len - i) != 12) || i > 3)
+ comerrno(EX_BAD, "ISRC '%s' has illegal length.\n", isrc);
+ }
+
+ /*
+ * The country code.
+ */
+ for (p = isrc, ip = ibuf, i = 0; i < 2; p++, i++) {
+ *ip++ = *p;
+ if (!strchr(upper, *p)) {
+/* goto illchar;*/
+ /*
+ * Flag numbers but accept them.
+ */
+ isrc_illchar(isrc, *p);
+ if (*p >= '0' && *p <= '9')
+ continue;
+ exit(EX_BAD);
+ }
+ }
+ if (*p == '-')
+ p++;
+
+ /*
+ * The owner code.
+ */
+ for (i = 0; i < 3; p++, i++) {
+ *ip++ = *p;
+ if (strchr(upper, *p))
+ continue;
+ if (*p >= '0' && *p <= '9')
+ continue;
+ goto illchar;
+ }
+ if (*p == '-')
+ p++;
+
+ /*
+ * The Year and the recording number (2 + 5 numbers).
+ */
+ for (i = 0; i < 7; p++, i++) {
+ *ip++ = *p;
+ if (*p >= '0' && *p <= '9')
+ continue;
+ if (*p == '-' && i == 2) {
+ ip--;
+ i--;
+ continue;
+ }
+ goto illchar;
+ }
+ *ip = '\0';
+ p = malloc(13);
+ strcpy(p, ibuf);
+ trackp->isrc = p;
+
+ if (debug)
+ printf("Track %d ISRC: '%s'\n", (int)trackp->trackno, trackp->isrc);
+ return;
+illchar:
+ isrc_illchar(isrc, *p);
+ exit(EX_BAD);
+}
+
+void
+setindex(char *tindex, track_t *trackp)
+{
+ char *p;
+ int i;
+ int nindex;
+ long idx;
+ long *idxlist;
+
+ idxlist = malloc(100*sizeof (long));
+ p = tindex;
+ idxlist[0] = 0;
+ i = 0;
+ while (*p) {
+ p = astol(p, &idx);
+ if (*p != '\0' && *p != ' ' && *p != '\t' && *p != ',')
+ goto illchar;
+ i++;
+ if (i > 99)
+ comerrno(EX_BAD, "Too many indices for track %d\n", (int)trackp->trackno);
+ idxlist[i] = idx;
+ if (*p == ',')
+ p++;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ }
+ nindex = i;
+
+ if (debug)
+ printf("Track %d %d Index: '%s'\n", (int)trackp->trackno, i, tindex);
+
+ if (debug) {
+ for (i = 0; i <= nindex; i++)
+ printf("%d: %ld\n", i, idxlist[i]);
+ }
+
+ trackp->nindex = nindex;
+ trackp->tindex = idxlist;
+ return;
+illchar:
+ comerrno(EX_BAD, "Index '%s' contains illegal character '%c'.\n", tindex, *p);
+}
diff --git a/wodim/cd_misc.c b/wodim/cd_misc.c
new file mode 100644
index 0000000..b9970bb
--- /dev/null
+++ b/wodim/cd_misc.c
@@ -0,0 +1,144 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cd_misc.c 1.10 01/10/29 Copyright 1997 J. Schilling */
+/*
+ * Misc CD support routines
+ *
+ * Copyright (c) 1997 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <standard.h>
+#include <utypes.h> /* Includes <sys/types.h> for caddr_t */
+#include <stdio.h>
+#include <schily.h>
+
+#include "wodim.h"
+
+int from_bcd(int b);
+int to_bcd(int i);
+long msf_to_lba(int m, int s, int f, BOOL force_positive);
+BOOL lba_to_msf(long lba, msf_t *mp);
+void sec_to_msf(long sec, msf_t *mp);
+void print_min_atip(long li, long lo);
+
+int
+from_bcd(int b)
+{
+ return ((b & 0x0F) + 10 * (((b)>> 4) & 0x0F));
+}
+
+int
+to_bcd(int i)
+{
+ return (i % 10 | ((i / 10) % 10) << 4);
+}
+
+long
+msf_to_lba(int m, int s, int f, BOOL force_positive)
+{
+ long ret = m * 60 + s;
+
+ ret *= 75;
+ ret += f;
+ if (m < 90 || force_positive)
+ ret -= 150;
+ else
+ ret -= 450150;
+ return (ret);
+}
+
+BOOL
+lba_to_msf(long lba, msf_t *mp)
+{
+ int m;
+ int s;
+ int f;
+
+#ifdef __follow_redbook__
+ if (lba >= -150 && lba < 405000) { /* lba <= 404849 */
+#else
+ if (lba >= -150) {
+#endif
+ m = (lba + 150) / 60 / 75;
+ s = (lba + 150 - m*60*75) / 75;
+ f = (lba + 150 - m*60*75 - s*75);
+
+ } else if (lba >= -45150 && lba <= -151) {
+ m = (lba + 450150) / 60 / 75;
+ s = (lba + 450150 - m*60*75) / 75;
+ f = (lba + 450150 - m*60*75 - s*75);
+
+ } else {
+ mp->msf_min = -1;
+ mp->msf_sec = -1;
+ mp->msf_frame = -1;
+
+ return (FALSE);
+ }
+ mp->msf_min = m;
+ mp->msf_sec = s;
+ mp->msf_frame = f;
+
+ if (lba > 404849) /* 404850 -> 404999: lead out */
+ return (FALSE);
+ return (TRUE);
+}
+
+void
+sec_to_msf(long sec, msf_t *mp)
+{
+ int m;
+ int s;
+ int f;
+
+ m = (sec) / 60 / 75;
+ s = (sec - m*60*75) / 75;
+ f = (sec - m*60*75 - s*75);
+
+ mp->msf_min = m;
+ mp->msf_sec = s;
+ mp->msf_frame = f;
+}
+
+void
+print_min_atip(long li, long lo)
+{
+ msf_t msf;
+
+ if (li < 0) {
+ lba_to_msf(li, &msf);
+
+ printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n",
+ li, msf.msf_min, msf.msf_sec, msf.msf_frame);
+ }
+ if (lo > 0) {
+ lba_to_msf(lo, &msf);
+ printf(" ATIP start of lead out: %ld (%02d:%02d/%02d)\n",
+ lo, msf.msf_min, msf.msf_sec, msf.msf_frame);
+ }
+}
diff --git a/wodim/cdr_drv.c b/wodim/cdr_drv.c
new file mode 100644
index 0000000..a40afa3
--- /dev/null
+++ b/wodim/cdr_drv.c
@@ -0,0 +1,318 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cdr_drv.c 1.36 04/03/02 Copyright 1997-2004 J. Schilling */
+/*
+ * CDR device abstraction layer
+ *
+ * Copyright (c) 1997-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* Include sys/types.h to make off_t available */
+#include <standard.h>
+#include <schily.h>
+
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+
+extern int xdebug;
+
+extern cdr_t cdr_oldcd;
+extern cdr_t cdr_cd;
+extern cdr_t cdr_mmc;
+extern cdr_t cdr_mdvd;
+extern cdr_t cdr_mmc_sony;
+extern cdr_t cdr_cd_dvd;
+extern cdr_t cdr_philips_cdd521O;
+extern cdr_t cdr_philips_dumb;
+extern cdr_t cdr_philips_cdd521;
+extern cdr_t cdr_philips_cdd522;
+extern cdr_t cdr_tyuden_ew50;
+extern cdr_t cdr_kodak_pcd600;
+extern cdr_t cdr_pioneer_dw_s114x;
+extern cdr_t cdr_plasmon_rf4100;
+extern cdr_t cdr_yamaha_cdr100;
+extern cdr_t cdr_sony_cdu924;
+extern cdr_t cdr_ricoh_ro1060;
+extern cdr_t cdr_ricoh_ro1420;
+extern cdr_t cdr_teac_cdr50;
+extern cdr_t cdr_cw7501;
+extern cdr_t cdr_cdr_simul;
+extern cdr_t cdr_dvd_simul;
+
+cdr_t *drive_identify(SCSI *usalp, cdr_t *, struct scsi_inquiry *ip);
+int drive_attach(SCSI *usalp, cdr_t *);
+int attach_unknown(void);
+int blank_dummy(SCSI *usalp, cdr_t *, long addr, int blanktype);
+int format_dummy(SCSI *usalp, cdr_t *, int fmtflags);
+int drive_getdisktype(SCSI *usalp, cdr_t *dp);
+int cmd_ill(SCSI *usalp);
+int cmd_dummy(SCSI *usalp, cdr_t *);
+int no_sendcue(SCSI *usalp, cdr_t *, track_t *trackp);
+int buf_dummy(SCSI *usalp, long *sp, long *fp);
+BOOL set_cdrcmds(char *name, cdr_t **dpp);
+cdr_t *get_cdrcmds(SCSI *usalp);
+
+/*
+ * List of CD-R drivers
+ */
+cdr_t *drivers[] = {
+ &cdr_cd_dvd,
+ &cdr_mmc,
+ &cdr_mdvd,
+ &cdr_mmc_sony,
+ &cdr_cd,
+ &cdr_oldcd,
+ &cdr_philips_cdd521O,
+ &cdr_philips_dumb,
+ &cdr_philips_cdd521,
+ &cdr_philips_cdd522,
+ &cdr_tyuden_ew50,
+ &cdr_kodak_pcd600,
+ &cdr_pioneer_dw_s114x,
+ &cdr_plasmon_rf4100,
+ &cdr_yamaha_cdr100,
+ &cdr_ricoh_ro1060,
+ &cdr_ricoh_ro1420,
+ &cdr_sony_cdu924,
+ &cdr_teac_cdr50,
+ &cdr_cw7501,
+ &cdr_cdr_simul,
+ &cdr_dvd_simul,
+ (cdr_t *)NULL,
+};
+
+cdr_t *
+drive_identify(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip)
+{
+ return (dp);
+}
+
+int
+drive_attach(SCSI *usalp, cdr_t *dp)
+{
+ return (0);
+}
+
+int
+attach_unknown()
+{
+ errmsgno(EX_BAD, "Unsupported drive type\n");
+ return (-1);
+}
+
+int
+blank_dummy(SCSI *usalp, cdr_t *dp, long addr, int blanktype)
+{
+ printf("This drive or media does not support the 'BLANK media' command\n");
+ return (-1);
+}
+
+int
+format_dummy(SCSI *usalp, cdr_t *dp, int fmtflags)
+{
+ printf("This drive or media does not support the 'FORMAT media' command\n");
+ return (-1);
+}
+
+int
+drive_getdisktype(SCSI *usalp, cdr_t *dp)
+{
+/* dstat_t *dsp = dp->cdr_dstat;*/
+ return (0);
+}
+
+int
+cmd_ill(SCSI *usalp)
+{
+ errmsgno(EX_BAD, "Unspecified command not implemented for this drive.\n");
+ return (-1);
+}
+
+int
+cmd_dummy(SCSI *usalp, cdr_t *dp)
+{
+ return (0);
+}
+
+int
+no_sendcue(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ errmsgno(EX_BAD, "SAO writing not available or not implemented for this drive.\n");
+ return (-1);
+}
+
+int
+buf_dummy(SCSI *usalp, long *sp, long *fp)
+{
+ return (-1);
+}
+
+BOOL
+set_cdrcmds(char *name, cdr_t **dpp)
+{
+ cdr_t **d;
+ int n;
+
+ for (d = drivers; *d != (cdr_t *)NULL; d++) {
+ if (streql((*d)->cdr_drname, name)) {
+ if (dpp != NULL)
+ *dpp = *d;
+ return (TRUE);
+ }
+ }
+ if (dpp == NULL)
+ return (FALSE);
+
+ if (!streql("help", name))
+ fprintf(stderr, "Illegal driver type '%s'.\n", name);
+
+ fprintf(stderr, "Driver types:\n");
+ for (d = drivers; *d != (cdr_t *)NULL; d++) {
+ fprintf(stderr, "%s%n",
+ (*d)->cdr_drname, &n);
+ fprintf(stderr, "%*s%s\n",
+ 20-n, "",
+ (*d)->cdr_drtext);
+ }
+ if (streql("help", name))
+ exit(0);
+ exit(EX_BAD);
+ return (FALSE); /* Make lint happy */
+}
+
+cdr_t *
+get_cdrcmds(SCSI *usalp)
+{
+ cdr_t *dp = (cdr_t *)0;
+ cdr_t *odp = (cdr_t *)0;
+ BOOL is_wr = FALSE;
+ BOOL is_cd = FALSE;
+ BOOL is_dvd = FALSE;
+ BOOL is_dvdplus = FALSE;
+ BOOL is_ddcd = FALSE;
+ BOOL is_cdwr = FALSE;
+ BOOL is_dvdwr = FALSE;
+ BOOL is_dvdpluswr = FALSE;
+ BOOL is_ddcdwr = FALSE;
+
+ /*
+ * First check for SCSI-3/mmc-3 drives.
+ */
+ if (get_proflist(usalp, &is_wr, &is_cd, &is_dvd,
+ &is_dvdplus, &is_ddcd) >= 0) {
+
+ get_wproflist(usalp, &is_cdwr, &is_dvdwr,
+ &is_dvdpluswr, &is_ddcdwr);
+ if (xdebug) {
+ fprintf(stderr,
+ "Found MMC-3 %s CD: %s/%s DVD-: %s/%s DVD+: %s/%s DDCD: %s/%s.\n",
+ is_wr ? "writer": "reader",
+ is_cd?"r":"-",
+ is_cdwr?"w":"-",
+ is_dvd?"r":"-",
+ is_dvdwr?"w":"-",
+ is_dvdplus?"r":"-",
+ is_dvdpluswr?"w":"-",
+ is_ddcd?"r":"-",
+ is_ddcdwr?"w":"-");
+ }
+ if (!is_wr) {
+ dp = &cdr_cd;
+ } else {
+ dp = &cdr_cd_dvd;
+ }
+ } else
+ /*
+ * First check for SCSI-3/mmc drives.
+ */
+ if (is_mmc(usalp, &is_cdwr, &is_dvdwr)) {
+ if (xdebug) {
+ fprintf(stderr, "Found MMC drive CDWR: %d DVDWR: %d.\n",
+ is_cdwr, is_dvdwr);
+ }
+
+ if (is_cdwr && is_dvdwr)
+ dp = &cdr_cd_dvd;
+ else
+ dp = &cdr_mmc;
+
+ } else switch (usalp->dev) {
+
+ case DEV_CDROM: dp = &cdr_oldcd; break;
+ case DEV_MMC_CDROM: dp = &cdr_cd; break;
+ case DEV_MMC_CDR: dp = &cdr_mmc; break;
+ case DEV_MMC_CDRW: dp = &cdr_mmc; break;
+ case DEV_MMC_DVD_WR: dp = &cdr_cd_dvd; break;
+
+ case DEV_CDD_521_OLD: dp = &cdr_philips_cdd521O; break;
+ case DEV_CDD_521: dp = &cdr_philips_cdd521; break;
+ case DEV_CDD_522:
+ case DEV_CDD_2000:
+ case DEV_CDD_2600: dp = &cdr_philips_cdd522; break;
+ case DEV_TYUDEN_EW50: dp = &cdr_tyuden_ew50; break;
+ case DEV_PCD_600: dp = &cdr_kodak_pcd600; break;
+ case DEV_YAMAHA_CDR_100:dp = &cdr_yamaha_cdr100; break;
+ case DEV_MATSUSHITA_7501:dp = &cdr_cw7501; break;
+ case DEV_MATSUSHITA_7502:
+ case DEV_YAMAHA_CDR_400:dp = &cdr_mmc; break;
+ case DEV_PLASMON_RF_4100:dp = &cdr_plasmon_rf4100; break;
+ case DEV_SONY_CDU_924: dp = &cdr_sony_cdu924; break;
+ case DEV_RICOH_RO_1060C:dp = &cdr_ricoh_ro1060; break;
+ case DEV_RICOH_RO_1420C:dp = &cdr_ricoh_ro1420; break;
+ case DEV_TEAC_CD_R50S: dp = &cdr_teac_cdr50; break;
+
+ case DEV_PIONEER_DW_S114X: dp = &cdr_pioneer_dw_s114x; break;
+
+ default: dp = &cdr_mmc;
+ }
+ odp = dp;
+
+ if (xdebug) {
+ fprintf(stderr, "Using driver '%s' for identify.\n",
+ dp != NULL ?
+ dp->cdr_drname :
+ "<no driver>");
+ }
+
+ if (dp != (cdr_t *)0)
+ dp = dp->cdr_identify(usalp, dp, usalp->inq);
+
+ if (xdebug && dp != odp) {
+ fprintf(stderr, "Identify set driver to '%s'.\n",
+ dp != NULL ?
+ dp->cdr_drname :
+ "<no driver>");
+ }
+
+ return (dp);
+}
diff --git a/wodim/cdtext.c b/wodim/cdtext.c
new file mode 100644
index 0000000..40df2a5
--- /dev/null
+++ b/wodim/cdtext.c
@@ -0,0 +1,546 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cdtext.c 1.10 04/03/01 Copyright 1999-2004 J. Schilling */
+/*
+ * Generic CD-Text support functions
+ *
+ * Copyright (c) 1999-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* Include sys/types.h to make off_t available */
+#include <standard.h>
+#include <utypes.h>
+#include <strdefs.h>
+#include <schily.h>
+
+#include <usal/scsitransp.h> /* For write_leadin() */
+
+#include "cdtext.h"
+#include "wodim.h"
+#include "crc16.h"
+
+#define PTI_TITLE 0x80 /* Album name and Track titles */
+#define PTI_PERFORMER 0x81 /* Singer/player/conductor/orchestra */
+#define PTI_SONGWRITER 0x82 /* Name of the songwriter */
+#define PTI_COMPOSER 0x83 /* Name of the composer */
+#define PTI_ARRANGER 0x84 /* Name of the arranger */
+#define PTI_MESSAGE 0x85 /* Message from content provider or artist */
+#define PTI_DISK_ID 0x86 /* Disk identification information */
+#define PTI_GENRE 0x87 /* Genre identification / information */
+#define PTI_TOC 0x88 /* TOC information */
+#define PTI_TOC2 0x89 /* Second TOC */
+#define PTI_RES_8A 0x8A /* Reserved 8A */
+#define PTI_RES_8B 0x8B /* Reserved 8B */
+#define PTI_RES_8C 0x8C /* Reserved 8C */
+#define PTI_CLOSED_INFO 0x8D /* For internal use by content provider */
+#define PTI_ISRC 0x8E /* UPC/EAN code of album and ISRC for tracks */
+#define PTI_SIZE 0x8F /* Size information of the block */
+
+extern int xdebug;
+
+typedef struct textpack {
+ Uchar pack_type; /* Pack Type indicator */
+ char track_no; /* Track Number (0..99) */
+ char seq_number; /* Sequence Number */
+ char block_number; /* Block # / Char pos */
+ char text[12]; /* CD-Text Data field */
+ char crc[2]; /* CRC 16 */
+} txtpack_t;
+
+#define EXT_DATA 0x80 /* Extended data indicator in track_no */
+#define DBCC 0x80 /* Double byte char indicator in block */
+
+/*
+ * CD-Text size example:
+ *
+ * 0 1 2 3 00 01 02 03 04 05 06 07 08 09 10 11 CRC16
+ *
+ * 8F 00 2B 00 01 01 0D 03 0C 0C 00 00 00 00 01 00 7B 3D
+ * 8F 01 2C 00 00 00 00 00 00 00 12 03 2D 00 00 00 DA B7
+ * 8F 02 2D 00 00 00 00 00 09 00 00 00 00 00 00 00 6A 24
+ *
+ * charcode 1
+ * first tr 1
+ * last tr 13
+ * Copyr 3
+ * Pack Count 80= 12, 81 = 12, 86 = 1, 8e = 18, 8f = 3
+ * last seq 0 = 2d
+ * languages 0 = 9
+ */
+
+typedef struct textsizes {
+ char charcode;
+ char first_track;
+ char last_track;
+ char copyr_flags;
+ char pack_count[16];
+ char last_seqnum[8];
+ char language_codes[8];
+} txtsize_t;
+
+typedef struct textargs {
+ txtpack_t *tp;
+ char *p;
+ txtsize_t *tsize;
+ int seqno;
+} txtarg_t;
+
+
+Uchar *textsub;
+int textlen;
+
+BOOL checktextfile(char *fname);
+static void setuptextdata(Uchar *bp, int len);
+static BOOL cdtext_crc_ok(struct textpack *p);
+void packtext(int tracks, track_t *trackp);
+static BOOL anytext(int pack_type, int tracks, track_t *trackp);
+static void fillup_pack(txtarg_t *ap);
+static void fillpacks(txtarg_t *ap, char *from, int len, int track_no, int pack_type);
+int write_cdtext(SCSI *usalp, cdr_t *dp, long startsec);
+static void eight2six(Uchar *in, Uchar *out);
+static void six2eight(Uchar *in, Uchar *out);
+
+
+BOOL checktextfile(char *fname)
+{
+ FILE *f;
+ Uchar hbuf[4];
+ Uchar *bp;
+ struct textpack *tp;
+ int len;
+ int crc;
+ int n;
+ int j;
+ off_t fs;
+
+ if ((f = fileopen(fname, "rb")) == NULL) {
+ errmsg("Cannot open '%s'.\n", fname);
+ return (FALSE);
+ }
+ fs = filesize(f);
+ j = fs % sizeof (struct textpack);
+ if (j == 4) {
+ n = fileread(f, hbuf, 4);
+ if (n != 4) {
+ if (n < 0)
+ errmsg("Cannot read '%s'.\n", fname);
+ else
+ errmsgno(EX_BAD, "File '%s' is too small for CD-Text.\n", fname);
+ return (FALSE);
+ }
+ len = hbuf[0] * 256 + hbuf[1];
+ len -= 2;
+ n = fs - 4;
+ if (n != len) {
+ errmsgno(EX_BAD, "Inconsistent CD-Text file '%s' length should be %d but is %lld\n",
+ fname, len+4, (Llong)fs);
+ return (FALSE);
+ }
+ } else if (j != 0) {
+ errmsgno(EX_BAD, "Inconsistent CD-Text file '%s' not a multiple of pack length\n",
+ fname);
+ return (FALSE);
+ } else {
+ len = fs;
+ }
+ printf("Text len: %d\n", len);
+ bp = malloc(len);
+ if (bp == NULL) {
+ errmsg("Cannot malloc CD-Text read buffer.\n");
+ return (FALSE);
+ }
+ n = fileread(f, bp, len);
+
+ tp = (struct textpack *)bp;
+ for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
+ if (tp->pack_type < 0x80 || tp->pack_type > 0x8F) {
+ errmsgno(EX_BAD, "Illegal pack type 0x%02X pack #%ld in CD-Text file '%s'.\n",
+ tp->pack_type, (long)(n/sizeof (struct textpack)), fname);
+ return (FALSE);
+ }
+ crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
+ crc ^= 0xFFFF;
+ if (crc != calcCRC((Uchar *)tp, sizeof (*tp)-2)) {
+ if (cdtext_crc_ok(tp)) {
+ errmsgno(EX_BAD,
+ "Corrected CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n",
+ (long)(n/sizeof (struct textpack)),
+ n+j, (long)(n+j+sizeof (struct textpack)),
+ fname);
+ } else {
+ errmsgno(EX_BAD, "CRC ERROR in pack #%ld (offset %d-%ld) in CD-Text file '%s'.\n",
+ (long)(n/sizeof (struct textpack)),
+ n+j, (long)(n+j+sizeof (struct textpack)),
+ fname);
+ return (FALSE);
+ }
+ }
+ }
+ setuptextdata(bp, len);
+ free(bp);
+
+ return (TRUE);
+}
+
+static void setuptextdata(Uchar *bp, int len)
+{
+ int n;
+ int i;
+ int j;
+ Uchar *p;
+
+ if (xdebug) {
+ printf("%ld packs %% 4 = %ld\n",
+ (long)(len/sizeof (struct textpack)),
+ (long)(len/sizeof (struct textpack)) % 4);
+ }
+ i = (len/sizeof (struct textpack)) % 4;
+ if (i == 0) {
+ n = len;
+ } else if (i == 2) {
+ n = 2 * len;
+ } else {
+ n = 4 * len;
+ }
+ n = (n * 4) / 3;
+ p = malloc(n);
+ if (p == NULL) {
+ errmsg("Cannot malloc CD-Text write buffer.\n");
+ }
+ for (i = 0, j = 0; j < n; ) {
+ eight2six(&bp[i%len], &p[j]);
+ i += 3;
+ j += 4;
+ }
+ textsub = p;
+ textlen = n;
+
+#ifdef DEBUG
+ {
+ Uchar sbuf[10000];
+ struct textpack *tp;
+ FILE *f;
+ int crc;
+
+ tp = (struct textpack *)bp;
+ p = sbuf;
+ for (n = 0; n < len; n += sizeof (struct textpack), tp++) {
+ crc = (tp->crc[0] & 0xFF) << 8 | (tp->crc[1] & 0xFF);
+ crc ^= 0xFFFF;
+
+ printf("Pack:%3d ", n/ sizeof (struct textpack));
+ printf("Pack type: %02X ", tp->pack_type & 0xFF);
+ printf("Track #: %2d ", tp->track_no & 0xFF);
+ printf("Sequence #:%3d ", tp->seq_number & 0xFF);
+ printf("Block #:%3d ", tp->block_number & 0xFF);
+ printf("CRC: %04X (%04X) ", crc, calcCRC((Uchar *)tp, sizeof (*tp)-2));
+ printf("Text: '%.12s'\n", tp->text);
+ movebytes(tp->text, p, 12);
+ p += 12;
+ }
+ printf("len total: %d\n", n);
+ f = fileopen("cdtext.out", "wctb");
+ if (f) {
+ filewrite(f, sbuf, p - sbuf);
+ fflush(f);
+ fclose(f);
+ }
+ }
+#endif
+}
+
+static BOOL cdtext_crc_ok(struct textpack *p)
+{
+ int crc;
+ struct textpack new;
+
+ movebytes(p, &new, sizeof (struct textpack));
+ new.crc[0] ^= 0xFF;
+ new.crc[1] ^= 0xFF;
+ crc = calcCRC((Uchar *)&new, sizeof (struct textpack));
+ crc = flip_crc_error_corr((Uchar *)&new, sizeof (struct textpack), crc);
+ new.crc[0] ^= 0xFF;
+ new.crc[1] ^= 0xFF;
+ if (crc == 0)
+ movebytes(&new, p, 18);
+
+ return (crc == 0);
+}
+
+
+void packtext(int tracks, track_t *trackp)
+{
+ int type;
+ int i;
+ struct textpack *tp;
+ struct textsizes tsize;
+ txtarg_t targ;
+ char sbuf[256*18];
+
+ fillbytes(sbuf, sizeof (sbuf), 0);
+ fillbytes(&tsize, sizeof (tsize), 0);
+
+ tsize.charcode = CC_8859_1; /* ISO-8859-1 */
+ tsize.first_track = trackp[1].trackno;
+ tsize.last_track = trackp[1].trackno + tracks - 1;
+#ifdef __FOUND_ON_COMMERCIAL_CD__
+ tsize.copyr_flags = 3; /* for titles/names */
+#else
+ tsize.copyr_flags = 0; /* no Copyr. limitat. */
+#endif
+ tsize.pack_count[0x0F] = 3; /* 3 size packs */
+ tsize.last_seqnum[0] = 0; /* Start value only */
+ tsize.language_codes[0] = LANG_ENGLISH; /* English */
+
+ tp = (struct textpack *)sbuf;
+
+ targ.tp = tp;
+ targ.p = NULL;
+ targ.tsize = &tsize;
+ targ.seqno = 0;
+
+ for (type = 0; type <= 0x0E; type++) {
+ register int maxtrk;
+ register char *s;
+
+ if (!anytext(type, tracks, trackp))
+ continue;
+ maxtrk = tsize.last_track;
+ if (type == 6) {
+ maxtrk = 0;
+ }
+ for (i = 0; i <= maxtrk; i++) {
+ s = trackp[i].text;
+ if (s)
+ s = ((textptr_t *)s)->textcodes[type];
+ if (s)
+ fillpacks(&targ, s, strlen(s)+1, i, 0x80| type);
+ else
+ fillpacks(&targ, "", 1, i, 0x80| type);
+
+ }
+ fillup_pack(&targ);
+ }
+
+ /*
+ * targ.seqno overshoots by one and we add 3 size packs...
+ */
+ tsize.last_seqnum[0] = targ.seqno + 2;
+
+ for (i = 0; i < 3; i++) {
+ fillpacks(&targ, &((char *)(&tsize))[i*12], 12, i, 0x8f);
+ }
+
+ setuptextdata((Uchar *)sbuf, targ.seqno*18);
+
+#ifdef DEBUG
+ { FILE *f;
+
+ f = fileopen("cdtext.new", "wctb");
+ if (f) {
+ filewrite(f, sbuf, targ.seqno*18);
+ fflush(f);
+ fclose(f);
+ }
+ }
+#endif
+}
+
+static BOOL anytext(int pack_type, int tracks, track_t *trackp)
+{
+ register int i;
+ register char *p;
+
+ for (i = 0; i <= tracks; i++) {
+ if (trackp[i].text == NULL)
+ continue;
+ p = ((textptr_t *)(trackp[i].text))->textcodes[pack_type];
+ if (p != NULL && *p != '\0')
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static void fillup_pack(register txtarg_t *ap)
+{
+ if (ap->p) {
+ fillbytes(ap->p, &ap->tp->text[12] - ap->p, '\0');
+ fillcrc((Uchar *)ap->tp, sizeof (*ap->tp));
+ ap->p = 0;
+ ap->tp++;
+ }
+}
+
+static void fillpacks(register txtarg_t *ap, register char *from, int len,
+ int track_no, int pack_type)
+{
+ register int charpos;
+ register char *p;
+ register txtpack_t *tp;
+
+ tp = ap->tp;
+ p = ap->p;
+ charpos = 0;
+ do {
+ if (p == 0) {
+ p = tp->text;
+ tp->pack_type = pack_type;
+ if (pack_type != 0x8f)
+ ap->tsize->pack_count[pack_type & 0x0F]++;
+ tp->track_no = track_no;
+ tp->seq_number = ap->seqno++;
+ if (charpos < 15)
+ tp->block_number = charpos;
+ else
+ tp->block_number = 15;
+ }
+ for (; --len >= 0 && p < &tp->text[12]; charpos++) {
+ *p++ = *from++;
+ }
+ len++; /* Overshoot compensation */
+
+ if (p >= &tp->text[12]) {
+ fillcrc((Uchar *)tp, sizeof (*tp));
+ p = 0;
+ tp++;
+ }
+ } while (len > 0);
+
+ ap->tp = tp;
+ ap->p = p;
+}
+
+int write_cdtext(SCSI *usalp, cdr_t *dp, long startsec)
+{
+ char *bp = (char *)textsub;
+ int buflen = textlen;
+ long amount;
+ long bytes = 0;
+ long end = -150;
+ int secspt = textlen / 96;
+ int bytespt = textlen;
+ long maxdma = usalp->maxbuf;
+ int idx;
+ int secs;
+ int nbytes;
+
+/*maxdma = 4320;*/
+ if (maxdma >= (2*textlen)) {
+ /*
+ * Try to make each CD-Text transfer use as much data
+ * as possible.
+ */
+ bp = usalp->bufptr;
+ for (idx = 0; (idx + textlen) <= maxdma; idx += textlen)
+ movebytes(textsub, &bp[idx], textlen);
+ buflen = idx;
+ secspt = buflen / 96;
+ bytespt = buflen;
+/*printf("textlen: %d buflen: %d secspt: %d\n", textlen, buflen, secspt);*/
+ } else if (maxdma < buflen) {
+ /*
+ * We have more CD-Text data than we may transfer at once.
+ */
+ secspt = maxdma / 96;
+ bytespt = secspt * 96;
+ }
+ while (startsec < end) {
+ if ((end - startsec) < secspt) {
+ secspt = end - startsec;
+ bytespt = secspt * 96;
+ }
+ idx = 0;
+ secs = secspt;
+ nbytes = bytespt;
+ do { /* loop over CD-Text data buffer */
+
+ if ((idx + nbytes) > buflen) {
+ nbytes = buflen - idx;
+ secs = nbytes / 96;
+ }
+/*printf("idx: %d nbytes: %d secs: %d startsec: %ld\n",*/
+/*idx, nbytes, secs, startsec);*/
+ amount = write_secs(usalp, dp,
+ (char *)&bp[idx], startsec, nbytes, secs, FALSE);
+ if (amount < 0) {
+ printf("write CD-Text data: error after %ld bytes\n",
+ bytes);
+ return (-1);
+ }
+ bytes += amount;
+ idx += amount;
+ startsec += secs;
+ } while (idx < buflen && startsec < end);
+ }
+ return (0);
+}
+
+
+/*
+ * 3 input bytes (8 bit based) are converted into 4 output bytes (6 bit based).
+ */
+static void eight2six(register Uchar *in, register Uchar *out)
+{
+ register int c;
+
+ c = in[0];
+ out[0] = (c >> 2) & 0x3F;
+ out[1] = (c & 0x03) << 4;
+
+ c = in[1];
+ out[1] |= (c & 0xF0) >> 4;
+ out[2] = (c & 0x0F) << 2;
+
+ c = in[2];
+ out[2] |= (c & 0xC0) >> 6;
+ out[3] = c & 0x3F;
+}
+
+/*
+ * 4 input bytes (6 bit based) are converted into 3 output bytes (8 bit based).
+ */
+static void six2eight(register Uchar *in, register Uchar *out)
+{
+ register int c;
+
+ c = in[0] & 0x3F;
+ out[0] = c << 2;
+
+ c = in[1] & 0x3F;
+ out[0] |= c >> 4;
+ out[1] = c << 4;
+
+ c = in[2] & 0x3F;
+ out[1] |= c >> 2;
+ out[2] = c << 6;
+
+ c = in[3] & 0x3F;
+ out[2] |= c;
+}
diff --git a/wodim/cdtext.h b/wodim/cdtext.h
new file mode 100644
index 0000000..89808c6
--- /dev/null
+++ b/wodim/cdtext.h
@@ -0,0 +1,143 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cdtext.h 1.5 04/03/02 Copyright 1999-2004 J. Schilling */
+/*
+ * Generic CD-Text support definitions
+ *
+ * Copyright (c) 1999-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef CDTEXT_H
+#define CDTEXT_H
+
+/*
+ * Strings for the CD-Text Pack Type indicators 0x80...0x8F
+ * We cannot use a plain structure here because we like to loop
+ * over all members.
+ */
+typedef struct textcodes {
+ char *textcodes[16];
+} textptr_t;
+
+#define tc_title textcodes[0x00]
+#define tc_performer textcodes[0x01]
+#define tc_songwriter textcodes[0x02]
+#define tc_composer textcodes[0x03]
+#define tc_arranger textcodes[0x04]
+#define tc_message textcodes[0x05]
+#define tc_diskid textcodes[0x06]
+#define tc_genre textcodes[0x07]
+#define tc_toc textcodes[0x08]
+#define tc_toc2 textcodes[0x09]
+
+#define tc_closed_info textcodes[0x0d]
+#define tc_isrc textcodes[0x0e]
+
+/*
+ * binaere Felder sind
+ * Disc ID (Wirklich ???)
+ * Genre ID
+ * TOC
+ * Second TOC
+ * Size information
+ */
+
+/*
+ * Genre codes from Enhanced CD Specification page 21
+ */
+#define GENRE_UNUSED 0 /* not used */
+#define GENRE_UNDEFINED 1 /* not defined */
+#define GENRE_ADULT_CONTEMP 2 /* Adult Contemporary */
+#define GENRE_ALT_ROCK 3 /* Alternative Rock */
+#define GENRE_CHILDRENS 4 /* Childrens Music */
+#define GENRE_CLASSIC 5 /* Classical */
+#define GENRE_CHRIST_CONTEMP 6 /* Contemporary Christian */
+#define GENRE_COUNTRY 7 /* Country */
+#define GENRE_DANCE 8 /* Dance */
+#define GENRE_EASY_LISTENING 9 /* Easy Listening */
+#define GENRE_EROTIC 10 /* Erotic */
+#define GENRE_FOLK 11 /* Folk */
+#define GENRE_GOSPEL 12 /* Gospel */
+#define GENRE_HIPHOP 13 /* Hip Hop */
+#define GENRE_JAZZ 14 /* Jazz */
+#define GENRE_LATIN 15 /* Latin */
+#define GENRE_MUSICAL 16 /* Musical */
+#define GENRE_NEWAGE 17 /* New Age */
+#define GENRE_OPERA 18 /* Opera */
+#define GENRE_OPERETTA 19 /* Operetta */
+#define GENRE_POP 20 /* Pop Music */
+#define GENRE_RAP 21 /* RAP */
+#define GENRE_REGGAE 22 /* Reggae */
+#define GENRE_ROCK 23 /* Rock Music */
+#define GENRE_RYTHMANDBLUES 24 /* Rhythm & Blues */
+#define GENRE_SOUNDEFFECTS 25 /* Sound Effects */
+#define GENRE_SPOKEN_WORD 26 /* Spoken Word */
+#define GENRE_WORLD_MUSIC 28 /* World Music */
+#define GENRE_RESERVED 29 /* Reserved is 29..32767 */
+#define GENRE_RIAA 32768 /* Registration by RIAA 32768..65535 */
+
+/*
+ * Character codings used in CD-Text data.
+ * Korean and Mandarin Chinese to be defined in sept 1996
+ */
+#define CC_8859_1 0x00 /* ISO 8859-1 */
+#define CC_ASCII 0x01 /* ISO 646, ASCII (7 bit) */
+#define CC_RESERVED_02 0x02 /* Reserved codes 0x02..0x7f */
+#define CC_KANJI 0x80 /* Music Shift-JIS Kanji */
+#define CC_KOREAN 0x81 /* Korean */
+#define CC_CHINESE 0x82 /* Mandarin Chinese */
+#define CC_RESERVED_83 0x83 /* Reserved codes 0x83..0xFF */
+
+
+/*
+ * The language code is encoded as specified in ANNEX 1 to part 5 of EBU
+ * Tech 32 58 -E (1991).
+ *
+ * The current language codes are guessed
+ */
+#define LANG_CZECH 6 /* 0x06 */
+#define LANG_DANISH 7 /* 0x07 */
+#define LANG_GERMAN 8 /* 0x08 */
+#define LANG_ENGLISH 9 /* 0x09 */
+#define LANG_SPANISH 10 /* 0x0A */
+#define LANG_FRENCH 15 /* 0x0F */
+#define LANG_ITALIAN 21 /* 0x15 */
+#define LANG_HUNGARIAN 27 /* 0x1B */
+#define LANG_DUTCH 29 /* 0x1D */
+#define LANG_NORWEGIAN 30 /* 0x1E */
+#define LANG_POLISH 32 /* 0x20 */
+#define LANG_PORTUGUESE 33 /* 0x21 */
+#define LANG_SLOVENE 38 /* 0x26 */
+#define LANG_FINNISH 39 /* 0x27 */
+#define LANG_SWEDISH 40 /* 0x28 */
+#define LANG_RUSSIAN 86 /* 0x56 */
+#define LANG_KOREAN 101 /* 0x65 */
+#define LANG_JAPANESE 105 /* 0x69 */
+#define LANG_GREEK 112 /* 0x70 */
+#define LANG_CHINESE 117 /* 0x75 */
+
+#endif
diff --git a/wodim/clone.c b/wodim/clone.c
new file mode 100644
index 0000000..da8fb91
--- /dev/null
+++ b/wodim/clone.c
@@ -0,0 +1,270 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)clone.c 1.7 04/03/02 Copyright 2001-2004 J. Schilling */
+/*
+ * Clone Subchannel processing
+ *
+ * Copyright (c) 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <fctldefs.h>
+#include <strdefs.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <btorder.h>
+#include <utypes.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+#include "crc16.h"
+
+#include <usal/scsireg.h>
+#include "scsimmc.h"
+
+/*#define SAO_RAW*/
+
+void clone_toc(track_t *trackp);
+void clone_tracktype(track_t *trackp);
+
+extern int lverbose;
+extern int xdebug;
+
+extern Uchar _subq[110][12];
+extern int _nsubh;
+
+
+static int ctrl_first;
+static int ctrl_last;
+static int sectype_first;
+static int sectype_last;
+static int disktype;
+static long loutstart;
+
+/*
+ * Read Clone TOC description from full toc file.
+ */
+void clone_toc(track_t *trackp)
+{
+ char filename[1024];
+ msf_t m;
+ msf_t mr;
+ struct tocheader *tp;
+ struct ftrackdesc *fp;
+ int f;
+ char buf[2048];
+ int amt;
+ int len;
+ int i;
+ int j;
+ int ctrladr;
+ Uint first = 100;
+ Uint last = 0;
+
+ len = strlen(trackp[1].filename);
+ if (len > (sizeof (filename)-5)) {
+ len = sizeof (filename)-5;
+ }
+ snprintf(filename, sizeof (filename), "%.*s.toc", len, trackp[1].filename);
+
+ f = open(filename, O_RDONLY|O_BINARY);
+ if (f < 0)
+ comerr("Cannot open '%s'.\n", filename);
+ amt = read(f, buf, sizeof (buf));
+
+ if (amt == sizeof (buf))
+ comerrno(EX_BAD, "TOC too large.\n");
+ close(f);
+ tp = (struct tocheader *)buf;
+ len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2;
+
+ if (xdebug) {
+ printf("Read %d bytes TOC len: %d first session: %d last session: %d\n",
+ amt, len, tp->first, tp->last);
+ }
+
+ fp = (struct ftrackdesc *)&buf[4];
+
+ for (i = 4, j = 0; i < len; i += 11) {
+ fp = (struct ftrackdesc *)&buf[i];
+ if (xdebug)
+ usal_prbytes("FT", (Uchar *)&buf[i], 11);
+ if (fp->sess_number != 1)
+ comerrno(EX_BAD, "Can only copy session # 1.\n");
+
+ if (fp->adr == 1) {
+ if (fp->point < first) {
+ first = fp->point;
+ ctrl_first = fp->control;
+ }
+ if (fp->point <= 99 && fp->point > last) {
+ last = fp->point;
+ ctrl_last = fp->control;
+ }
+ }
+ if (fp->adr != 1) {
+ switch (fp->point) {
+
+ case 0xB0:
+ case 0xC0:
+ case 0xC1:
+ break;
+ default:
+ continue;
+ }
+ }
+ m.msf_min = fp->amin;
+ m.msf_sec = fp->asec;
+ m.msf_frame = fp->aframe;
+
+ mr.msf_min = fp->pmin;
+ mr.msf_sec = fp->psec;
+ mr.msf_frame = fp->pframe;
+
+ if (fp->point == 0xA0) {
+ disktype = mr.msf_sec;
+ mr.msf_sec = from_bcd(mr.msf_sec); /* convert to BCD */
+ }
+
+ if (fp->point == 0xA2)
+ loutstart = msf_to_lba(fp->pmin, fp->psec, fp->pframe, TRUE);
+ ctrladr = fp->control << 4;
+ ctrladr |= fp->adr;
+
+ filltpoint(_subq[j], ctrladr, fp->point, &mr);
+ fillttime(_subq[j], &m);
+ _subq[j][6] = fp->res7;
+ if (fp->point == 0xC0 || fp->point == 0xC1) {
+ _subq[j][3] = m.msf_min;
+ _subq[j][4] = m.msf_sec;
+ _subq[j][5] = m.msf_frame;
+ }
+ if (fp->point == 0xC1) {
+ _subq[j][7] = mr.msf_min;
+ _subq[j][8] = mr.msf_sec;
+ _subq[j][9] = mr.msf_frame;
+ }
+ if (xdebug)
+ usal_prbytes("TOC ", _subq[j], 12);
+ j++;
+ }
+ _nsubh = j;
+ if (xdebug) {
+ printf("nsubheader %d lout: %ld track 1 secs: %ld\n", j, loutstart, trackp[1].tracksecs);
+ printf("first %u last %u ctrl first: %X ctrl last %X\n", first, last, ctrl_first, ctrl_last);
+ }
+ if (trackp->tracks != 1)
+ comerrno(EX_BAD, "Clone writing currently supports only one file argument.\n");
+ if (loutstart > trackp[1].tracksecs)
+ comerrno(EX_BAD, "Clone writing TOC length %ld does not match track length %ld\n",
+ loutstart, trackp[1].tracksecs);
+
+ if (amt > len) {
+ sectype_first = buf[len];
+ sectype_last = buf[len+1];
+ if (xdebug) {
+ printf("sectype first: %X sectype last %X\n",
+ sectype_first, sectype_last);
+ }
+ }
+}
+
+
+/*
+ * Set tracktypes for track 0 (lead-in) & track AA (lead-out)
+ *
+ * Control 0 = audio
+ * Control 1 = audio preemp
+ * Control 2 = audio copy
+ * Control 3 = audio copy preemp
+ * Control 4 = data
+ * Control 5 = packet data
+ */
+void clone_tracktype(track_t *trackp)
+{
+ int tracks = trackp->tracks;
+ int sectype;
+
+ sectype = SECT_ROM;
+ if ((ctrl_first & TM_DATA) == 0) {
+ sectype = SECT_AUDIO;
+
+ if ((ctrl_first & TM_PREEM) != 0) {
+ trackp[0].flags |= TI_PREEMP;
+ } else {
+ trackp[0].flags &= ~TI_PREEMP;
+ sectype |= ST_PREEMPMASK;
+ }
+ if ((ctrl_first & TM_ALLOW_COPY) != 0) {
+ trackp[0].flags |= TI_COPY;
+ } else {
+ trackp[0].flags &= ~TI_COPY;
+ }
+/* XXX ??? flags |= TI_SCMS; */
+ } else {
+ if ((ctrl_first & TM_INCREMENTAL) != 0) {
+ trackp[0].flags |= TI_PACKET;
+ } else {
+ trackp[0].flags &= ~TI_PACKET;
+ }
+ if (sectype_first != 0)
+ sectype = sectype_first;
+ }
+ trackp[0].sectype = sectype;
+
+ sectype = SECT_ROM;
+
+ if ((ctrl_last & TM_DATA) == 0) {
+ sectype = SECT_AUDIO;
+
+ if ((ctrl_last & TM_PREEM) != 0) {
+ trackp[tracks+1].flags |= TI_PREEMP;
+ } else {
+ trackp[tracks+1].flags &= ~TI_PREEMP;
+ sectype |= ST_PREEMPMASK;
+ }
+ if ((ctrl_last & TM_ALLOW_COPY) != 0) {
+ trackp[tracks+1].flags |= TI_COPY;
+ } else {
+ trackp[tracks+1].flags &= ~TI_COPY;
+ }
+/* XXX ??? flags |= TI_SCMS; */
+ } else {
+ if ((ctrl_first & TM_INCREMENTAL) != 0) {
+ trackp[0].flags |= TI_PACKET;
+ } else {
+ trackp[0].flags &= ~TI_PACKET;
+ if (sectype_last != 0)
+ sectype = sectype_last;
+ }
+ }
+ trackp[tracks+1].sectype = sectype;
+}
diff --git a/wodim/crc16.c b/wodim/crc16.c
new file mode 100644
index 0000000..8eecf04
--- /dev/null
+++ b/wodim/crc16.c
@@ -0,0 +1,160 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)crc16.c 1.6 04/03/02 Copyright 1998-2004 J. Schilling */
+/*
+ * Q-subchannel CRC subroutines
+ *
+ * Polynom is: p(x) = x ** 16 + x ** 12 + x ** 5 + 1
+ * If computed over 12 bytes, the result must be zero.
+ * On the disk the CRC bits are inverted.
+ *
+ * Copyright (c) 1998-2003 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <standard.h>
+#include <utypes.h>
+#include "crc16.h"
+
+static UInt16_t updcrc(Uint p_crc, UInt8_t *cp, Uint cnt);
+UInt16_t calcCRC(Uchar *buf, Uint bsize);
+UInt16_t fillcrc(Uchar *buf, Uint bsize);
+UInt16_t flip_crc_error_corr(Uchar *b, Uint bsize, Uint p_crc);
+
+
+ /* number of bits in CRC: don't change it. */
+#define BPW 16
+
+ /* this the number of bits per char: don't change it. */
+#define BPB 8
+
+static UInt16_t crctab[1<<BPB] = {
+ 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
+ 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
+ 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
+ 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
+ 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
+ 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
+ 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
+ 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
+ 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
+ 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
+ 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
+ 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
+ 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
+ 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
+ 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
+ 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
+ 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
+ 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
+ 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
+ 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
+ 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
+ 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
+ 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
+ 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
+ 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
+ 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
+ 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
+ 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
+ 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
+ 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
+ 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
+ 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
+};
+
+#define SUBSIZE 96 /* 12 bytes with 8 bits */
+
+static UInt16_t
+updcrc(Uint p_crc, register UInt8_t *cp, register Uint cnt)
+{
+ register UInt16_t crc = p_crc;
+
+ while (cnt-- > 0) {
+ crc = (crc<<BPB) ^ crctab[(crc>>(BPW-BPB)) ^ (*cp++)];
+ }
+ return (crc);
+}
+
+/*static UInt16_t*/
+UInt16_t
+calcCRC(Uchar *buf, Uint bsize)
+{
+ return (updcrc(0x0000, (UInt8_t *)buf, bsize));
+}
+
+/*
+ * CRC für Q-Sub füllen
+ */
+UInt16_t
+fillcrc(Uchar *buf, Uint bsize)
+{
+ UInt16_t crc = calcCRC(buf, bsize-2);
+
+ /*
+ * Invert CRC bits for RED Book compliance.
+ */
+ crc = crc ^ 0xFFFF;
+
+ buf[bsize-2] = (crc >> 8) & 0xFF;
+ buf[bsize-1] = crc & 0xFF;
+ return (crc);
+}
+
+static UInt8_t fliptab[BPB] = {
+ 0x01,
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80,
+};
+
+UInt16_t
+flip_crc_error_corr(Uchar *b, Uint bsize, Uint p_crc)
+{
+ register UInt16_t crc = p_crc;
+ register Uint btsize = bsize * BPB;
+
+ if (crc != 0) {
+ int i;
+
+ for (i = 0; i < btsize; i++) {
+ UInt8_t c;
+
+ c = fliptab[i % BPB];
+ b[i / BPB] ^= c;
+ if ((crc = calcCRC(b, bsize)) == 0) {
+ return (crc);
+ }
+ b[i / BPB] ^= c;
+ }
+ }
+ return (crc & 0xffff);
+}
diff --git a/wodim/crc16.h b/wodim/crc16.h
new file mode 100644
index 0000000..c8f425c
--- /dev/null
+++ b/wodim/crc16.h
@@ -0,0 +1,41 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)crc16.h 1.3 02/03/04 Copyright 1998-2002 J. Schilling */
+/*
+ * Q-subchannel CRC definitions
+ *
+ * Copyright (c) 1998-2002 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _CRC16_H
+#define _CRC16_H
+
+extern UInt16_t calcCRC(Uchar *buf, Uint bsize);
+extern UInt16_t fillcrc(Uchar *buf, Uint bsize);
+extern UInt16_t flip_crc_error_corr(Uchar *b, Uint bsize, Uint p_crc);
+
+#endif
diff --git a/wodim/cue.c b/wodim/cue.c
new file mode 100644
index 0000000..0eb88b3
--- /dev/null
+++ b/wodim/cue.c
@@ -0,0 +1,1198 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cue.c 1.20 04/03/02 Copyright 2001-2004 J. Schilling */
+/*
+ * Cue sheet parser
+ *
+ * Copyright (c) 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <fctldefs.h>
+#include <statdefs.h>
+#include <vadefs.h>
+#include <schily.h>
+#include <strdefs.h>
+#include <utypes.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "xio.h"
+#include "cdtext.h"
+#include "wodim.h"
+#include "auheader.h"
+#include "libport.h"
+
+typedef struct state {
+ char *filename;
+ void *xfp;
+ Llong trackoff;
+ Llong filesize;
+ int filetype;
+ int tracktype;
+ int sectype;
+ int dbtype;
+ int secsize;
+ int dataoff;
+ int state;
+ int track;
+ int index;
+ long index0;
+ long index1; /* Current index 1 value */
+ long secoff; /* Old index 1 value */
+ long pregapsize;
+ long postgapsize;
+ int flags;
+} state_t;
+
+static char linebuf[4096];
+static char *fname;
+static char *linep;
+static char *wordendp;
+static char wordendc;
+static int olinelen;
+static int linelen;
+static int lineno;
+
+static char worddelim[] = "=:,/";
+static char nulldelim[] = "";
+
+#define STATE_NONE 0
+#define STATE_POSTGAP 1
+#define STATE_TRACK 2
+#define STATE_FLAGS 3
+#define STATE_INDEX0 4
+#define STATE_INDEX1 5
+
+typedef struct keyw {
+ char *k_name;
+ int k_type;
+} keyw_t;
+
+/*
+ * Keywords (first word on line):
+ * CATALOG - global CATALOG <MCN>
+ * CDTEXTFILE - global CDTEXTFILE <fname>
+ * FILE - track static FILE <fame> <type>
+ * FLAGS - track static FLAGS <flag> ...
+ * INDEX - track static INDEX <#> <mm:ss:ff>
+ * ISRC - track static ISRC <ISRC>
+ * PERFORMER - global/static PERFORMER <string>
+ * POSTGAP - track locak POSTGAP <mm:ss:ff>
+ * PREGAP - track static PREGAP <mm:ss:ff>
+ * REM - anywhere REM <comment>
+ * SONGWRITER - global/static SONGWRITER <string>
+ * TITLE - global/static TITLE <string>
+ * TRACK - track static TRACK <#> <datatype>
+ *
+ * Order of keywords:
+ * CATALOG
+ * CDTEXTFILE
+ * PERFORMER | SONGWRITER | TITLE Doc says past FILE...
+ * FILE Must be past CATALOG
+ * ------- Repeat the following: mehrere FILE Commands?
+ * TRACK
+ * FLAGS | ISRC | PERFORMER | PREGAP | SONGWRITER | TITLE
+ * INDEX
+ * POSTGAP
+ */
+
+#define K_G 0x10000 /* Global */
+#define K_T 0x20000 /* Track static */
+#define K_A (K_T | K_G) /* Global & Track static */
+
+#define K_MCN (0 | K_G) /* Media catalog number */
+#define K_TEXTFILE (1 | K_G) /* CD-Text binary file */
+#define K_FILE (2 | K_T) /* Input data file */
+#define K_FLAGS (3 | K_T) /* Flags for ctrl nibble */
+#define K_INDEX (4 | K_T) /* Index marker for track */
+#define K_ISRC (5 | K_T) /* ISRC string for track */
+#define K_PERFORMER (6 | K_A) /* CD-Text Performer */
+#define K_POSTGAP (7 | K_T) /* Post gap for track (autogen) */
+#define K_PREGAP (8 | K_T) /* Pre gap for track (autogen) */
+#define K_REM (9 | K_A) /* Remark (Comment) */
+#define K_SONGWRITER (10| K_A) /* CD-Text Songwriter */
+#define K_TITLE (11| K_A) /* CD-Text Title */
+#define K_TRACK (12| K_T) /* Track marker */
+
+
+static keyw_t keywords[] = {
+ { "CATALOG", K_MCN },
+ { "CDTEXTFILE", K_TEXTFILE },
+ { "FILE", K_FILE },
+ { "FLAGS", K_FLAGS },
+ { "INDEX", K_INDEX },
+ { "ISRC", K_ISRC },
+ { "PERFORMER", K_PERFORMER },
+ { "POSTGAP", K_POSTGAP },
+ { "PREGAP", K_PREGAP },
+ { "REM", K_REM },
+ { "SONGWRITER", K_SONGWRITER },
+ { "TITLE", K_TITLE },
+ { "TRACK", K_TRACK },
+ { NULL, 0 },
+};
+
+
+/*
+ * Filetypes - argument to FILE Keyword (one only):
+ *
+ * BINARY - Intel binary file (least significant byte first)
+ * MOTOTOLA - Motorola binary file (most significant byte first)
+ * AIFF - Audio AIFF file
+ * AU - Sun Audio file
+ * WAVE - Audio WAVE file
+ * MP3 - Audio MP3 file
+ */
+#define K_BINARY 100
+#define K_MOTOROLA 101
+#define K_AIFF 102
+#define K_AU 103
+#define K_WAVE 104
+#define K_MP3 105
+#define K_OGG 106
+
+static keyw_t filetypes[] = {
+ { "BINARY", K_BINARY },
+ { "MOTOROLA", K_MOTOROLA },
+ { "AIFF", K_AIFF },
+ { "AU", K_AU },
+ { "WAVE", K_WAVE },
+ { "MP3", K_MP3 },
+ { "OGG", K_OGG },
+ { NULL, 0 },
+};
+
+/*
+ * Flags - argument to FLAGS Keyword (more than one allowed):
+ * DCP - Digital copy permitted
+ * 4CH - Four channel audio
+ * PRE - Pre-emphasis enabled (audio tracks only)
+ * SCMS - Serial copy management system (not supported by all recorders)
+ */
+#define K_DCP 1000
+#define K_4CH 1001
+#define K_PRE 1002
+#define K_SCMS 1003
+
+static keyw_t flags[] = {
+ { "DCP", K_DCP },
+ { "4CH", K_4CH },
+ { "PRE", K_PRE },
+ { "SCMS", K_SCMS },
+ { NULL, 0 },
+};
+
+/*
+ * Datatypes - argument to TRACK Keyword (one only):
+ * AUDIO - Audio/Music (2352)
+ * CDG - Karaoke CD+G (2448)
+ * MODE1/2048 - CDROM Mode1 Data (cooked)
+ * MODE1/2352 - CDROM Mode1 Data (raw)
+ * MODE2/2336 - CDROM-XA Mode2 Data
+ * MODE2/2352 - CDROM-XA Mode2 Data
+ * CDI/2336 - CDI Mode2 Data
+ * CDI/2352 - CDI Mode2 Data
+ */
+#define K_AUDIO 10000
+#define K_CDG 10001
+#define K_MODE1 10002
+#define K_MODE2 10003
+#define K_CDI 10004
+
+static keyw_t dtypes[] = {
+ { "AUDIO", K_AUDIO },
+ { "CDG", K_CDG },
+ { "MODE1", K_MODE1 },
+ { "MODE2", K_MODE2 },
+ { "CDI", K_CDI },
+ { NULL, 0 },
+};
+
+
+int parsecue(char *cuefname, track_t trackp[]);
+void fparsecue(FILE *f, track_t trackp[]);
+static void parse_mcn(track_t trackp[], state_t *sp);
+static void parse_textfile(track_t trackp[], state_t *sp);
+static void parse_file(track_t trackp[], state_t *sp);
+static void parse_flags(track_t trackp[], state_t *sp);
+static void parse_index(track_t trackp[], state_t *sp);
+static void parse_isrc(track_t trackp[], state_t *sp);
+static void parse_performer(track_t trackp[], state_t *sp);
+static void parse_postgap(track_t trackp[], state_t *sp);
+static void parse_pregap(track_t trackp[], state_t *sp);
+static void parse_songwriter(track_t trackp[], state_t *sp);
+static void parse_title(track_t trackp[], state_t *sp);
+static void parse_track(track_t trackp[], state_t *sp);
+static void parse_offset(long *lp);
+static void newtrack(track_t trackp[], state_t *sp);
+
+static keyw_t *lookup(char *word, keyw_t table[]);
+static void wdebug(void);
+static FILE *cueopen(char *name);
+static char *cuename(void);
+static char *nextline(FILE *f);
+static void ungetline(void);
+static char *skipwhite(const char *s);
+static char *peekword(void);
+static char *lineend(void);
+static char *markword(char *delim);
+static char *getnextitem(char *delim);
+static char *neednextitem(char *delim);
+static char *nextword(void);
+static char *needword(void);
+static char *curword(void);
+static char *nextitem(void);
+static char *needitem(void);
+static void checkextra(void);
+static void cueabort(const char *fmt, ...);
+
+#ifdef CUE_MAIN
+int debug;
+int xdebug = 1;
+
+int write_secs(void);
+int write_secs() { return (-1); }
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ track_t track[MAX_TRACK+2]; /* Max tracks + track 0 + track AA */
+
+ save_args(argc, argv);
+
+ fillbytes(track, sizeof (track), '\0');
+ for (i = 0; i < MAX_TRACK+2; i++)
+ track[i].track = track[i].trackno = i;
+ track[0].tracktype = TOC_MASK;
+
+
+ parsecue(argv[1], track);
+ return (0);
+}
+#else
+extern int xdebug;
+#endif
+
+int
+parsecue(char *cuefname, track_t trackp[])
+{
+ FILE *f = cueopen(cuefname);
+
+ fparsecue(f, trackp);
+ return (0);
+}
+
+void
+fparsecue(FILE *f, track_t trackp[])
+{
+ char *word;
+ struct keyw *kp;
+ BOOL isglobal = TRUE;
+ state_t state;
+
+ state.filename = NULL;
+ state.xfp = NULL;
+ state.trackoff = 0;
+ state.filesize = 0;
+ state.filetype = 0;
+ state.tracktype = 0;
+ state.sectype = 0;
+ state.dbtype = 0;
+ state.secsize = 0;
+ state.dataoff = 0;
+ state.state = STATE_NONE;
+ state.track = 0;
+ state.index = -1;
+ state.index0 = -1;
+ state.index1 = -1;
+ state.secoff = 0;
+ state.pregapsize = -1;
+ state.postgapsize = -1;
+ state.flags = 0;
+
+ if (xdebug > 1)
+ printf("---> Entering CUE Parser...\n");
+ do {
+ if (nextline(f) == NULL) {
+ /*
+ * EOF on CUE File
+ * Do post processing here
+ */
+ if (state.state < STATE_INDEX1)
+ cueabort("Incomplete CUE file");
+ if (state.xfp)
+ xclose(state.xfp);
+ if (xdebug > 1) {
+ printf("---> CUE Parser got EOF, found %d tracks.\n",
+ state.track);
+ }
+ return;
+ }
+ word = nextitem();
+ if (*word == '\0') /* empty line */
+ continue;
+
+ if (xdebug > 1)
+ printf("\nKEY: '%s' %s\n", word, peekword());
+ kp = lookup(word, keywords);
+ if (kp == NULL)
+ cueabort("Unknown CUE keyword '%s'", word);
+
+ if ((kp->k_type & K_G) == 0) {
+ if (isglobal)
+ isglobal = FALSE;
+ }
+ if ((kp->k_type & K_T) == 0) {
+ if (!isglobal)
+ cueabort("Badly placed CUE keyword '%s'", word);
+ }
+/* printf("%s-", isglobal ? "G" : "T");*/
+/* wdebug();*/
+
+ switch (kp->k_type) {
+
+ case K_MCN: parse_mcn(trackp, &state); break;
+ case K_TEXTFILE: parse_textfile(trackp, &state); break;
+ case K_FILE: parse_file(trackp, &state); break;
+ case K_FLAGS: parse_flags(trackp, &state); break;
+ case K_INDEX: parse_index(trackp, &state); break;
+ case K_ISRC: parse_isrc(trackp, &state); break;
+ case K_PERFORMER: parse_performer(trackp, &state); break;
+ case K_POSTGAP: parse_postgap(trackp, &state); break;
+ case K_PREGAP: parse_pregap(trackp, &state); break;
+ case K_REM: break;
+ case K_SONGWRITER: parse_songwriter(trackp, &state); break;
+ case K_TITLE: parse_title(trackp, &state); break;
+ case K_TRACK: parse_track(trackp, &state); break;
+
+ default:
+ cueabort("Panic: unknown CUE command '%s'", word);
+ }
+ } while (1);
+}
+
+static void
+parse_mcn(track_t trackp[], state_t *sp)
+{
+ char *word;
+ textptr_t *txp;
+
+ if (sp->track != 0)
+ cueabort("CATALOG keyword must be before first TRACK");
+
+ word = needitem();
+ setmcn(word, &trackp[0]);
+ txp = gettextptr(0, trackp); /* MCN is isrc for trk 0 */
+ txp->tc_isrc = strdup(word);
+
+ checkextra();
+}
+
+static void
+parse_textfile(track_t trackp[], state_t *sp)
+{
+ char *word;
+
+ if (sp->track != 0)
+ cueabort("CDTEXTFILE keyword must be before first TRACK");
+
+ word = needitem();
+
+ if (trackp[MAX_TRACK+1].flags & TI_TEXT) {
+ if (!checktextfile(word)) {
+ comerrno(EX_BAD,
+ "Cannot use '%s' as CD-Text file.\n",
+ word);
+ }
+ trackp[0].flags |= TI_TEXT;
+ } else {
+ errmsgno(EX_BAD, "Ignoring CDTEXTFILE '%s'.\n", word);
+ errmsgno(EX_BAD, "If you like to write CD-Text, call wodim -text.\n");
+ }
+
+ checkextra();
+}
+
+static void
+parse_file(track_t trackp[], state_t *sp)
+{
+ char cname[1024];
+ char newname[1024];
+ struct keyw *kp;
+ char *word;
+ char *filetype;
+ struct stat st;
+#ifdef hint
+ Llong lsize;
+#endif
+
+ if (sp->filename != NULL)
+ cueabort("Only one FILE allowed");
+
+ word = needitem();
+ if (sp->xfp)
+ xclose(sp->xfp);
+ sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0);
+ if (sp->xfp == NULL && geterrno() == ENOENT) {
+ char *p;
+
+ if (strchr(word, '/') == 0 &&
+ strchr(cuename(), '/') != 0) {
+ snprintf(cname, sizeof (cname),
+ "%s", cuename());
+ p = strrchr(cname, '/');
+ if (p)
+ *p = '\0';
+ snprintf(newname, sizeof (newname),
+ "%s/%s", cname, word);
+ word = newname;
+ sp->xfp = xopen(word, O_RDONLY|O_BINARY, 0);
+ }
+ }
+ if (sp->xfp == NULL)
+ comerr("Cannot open FILE '%s'.\n", word);
+
+ sp->filename = strdup(word);
+ sp->trackoff = 0;
+ sp->filesize = 0;
+ sp->flags &= ~TI_SWAB; /* Reset what we might set for FILE */
+
+ filetype = needitem();
+ kp = lookup(filetype, filetypes);
+ if (kp == NULL)
+ cueabort("Unknown filetype '%s'", filetype);
+
+ switch (kp->k_type) {
+
+ case K_BINARY:
+ case K_MOTOROLA:
+ if (fstat(xfileno(sp->xfp), &st) >= 0 &&
+ S_ISREG(st.st_mode)) {
+ sp->filesize = st.st_size;
+ } else {
+ cueabort("Unknown file size for FILE '%s'",
+ sp->filename);
+ }
+ break;
+ case K_AIFF:
+ cueabort("Unsupported filetype '%s'", kp->k_name);
+ break;
+ case K_AU:
+ sp->filesize = ausize(xfileno(sp->xfp));
+ break;
+ case K_WAVE:
+ sp->filesize = wavsize(xfileno(sp->xfp));
+ sp->flags |= TI_SWAB;
+ break;
+ case K_MP3:
+ case K_OGG:
+ cueabort("Unsupported filetype '%s'", kp->k_name);
+ break;
+
+ default: cueabort("Panic: unknown filetype '%s'", filetype);
+ }
+
+ if (sp->filesize == AU_BAD_CODING) {
+ cueabort("Inappropriate audio coding in '%s'",
+ sp->filename);
+ }
+ if (xdebug > 0)
+ printf("Track %d File '%s' Filesize %lld\n",
+ sp->track, sp->filename, sp->filesize);
+
+ sp->filetype = kp->k_type;
+
+ checkextra();
+
+
+#ifdef hint
+ trackp->itracksize = lsize;
+ if (trackp->itracksize != lsize)
+ comerrno(EX_BAD, "This OS cannot handle large audio images.\n");
+#endif
+}
+
+static void
+parse_flags(track_t trackp[], state_t *sp)
+{
+ struct keyw *kp;
+ char *word;
+
+ if ((sp->state < STATE_TRACK) ||
+ (sp->state >= STATE_INDEX0))
+ cueabort("Badly placed FLAGS keyword");
+ sp->state = STATE_FLAGS;
+
+ do {
+ word = needitem();
+ kp = lookup(word, flags);
+ if (kp == NULL)
+ cueabort("Unknown flag '%s'", word);
+
+ switch (kp->k_type) {
+
+ case K_DCP: sp->flags |= TI_COPY; break;
+ case K_4CH: sp->flags |= TI_QUADRO; break;
+ case K_PRE: sp->flags |= TI_PREEMP; break;
+ case K_SCMS: sp->flags |= TI_SCMS; break;
+ default: cueabort("Panic: unknown FLAG '%s'", word);
+ }
+
+ } while (peekword() < lineend());
+
+ if (xdebug > 0)
+ printf("Track %d flags 0x%08X\n", sp->track, sp->flags);
+}
+
+static void
+parse_index(track_t trackp[], state_t *sp)
+{
+ char *word;
+ long l;
+ int track = sp->track;
+
+ if (sp->state < STATE_TRACK)
+ cueabort("Badly placed INDEX keyword");
+
+
+ word = needitem();
+ if (*astolb(word, &l, 10) != '\0')
+ cueabort("Not a number '%s'", word);
+ if (l < 0 || l > 99)
+ cueabort("Illegal index '%s'", word);
+
+ if ((sp->index < l) &&
+ (((sp->index + 1) == l) || l == 1))
+ sp->index = l;
+ else
+ cueabort("Badly placed INDEX %ld number", l);
+
+ if (l > 0)
+ sp->state = STATE_INDEX1;
+ else
+ sp->state = STATE_INDEX0;
+
+ parse_offset(&l);
+
+ if (xdebug > 1)
+ printf("Track %d Index %d %ld\n", sp->track, sp->index, l);
+
+ if (sp->index == 0)
+ sp->index0 = l;
+ if (sp->index == 1) {
+ sp->index1 = l;
+ trackp[track].nindex = 1;
+ newtrack(trackp, sp);
+
+ if (xdebug > 1) {
+ printf("Track %d pregapsize %ld\n",
+ sp->track, trackp[track].pregapsize);
+ }
+ }
+ if (sp->index == 2) {
+ trackp[track].tindex = malloc(100*sizeof (long));
+ trackp[track].tindex[1] = 0;
+ trackp[track].tindex[2] = l - sp->index1;
+ trackp[track].nindex = 2;
+ }
+ if (sp->index > 2) {
+ trackp[track].tindex[sp->index] = l - sp->index1;
+ trackp[track].nindex = sp->index;
+ }
+
+ checkextra();
+}
+
+static void
+parse_isrc(track_t trackp[], state_t *sp)
+{
+ char *word;
+ textptr_t *txp;
+ int track = sp->track;
+
+ if (track == 0)
+ cueabort("ISRC keyword must be past first TRACK");
+
+ if ((sp->state < STATE_TRACK) ||
+ (sp->state >= STATE_INDEX0))
+ cueabort("Badly placed ISRC keyword");
+ sp->state = STATE_FLAGS;
+
+ word = needitem();
+ setisrc(word, &trackp[track]);
+ txp = gettextptr(track, trackp);
+ txp->tc_isrc = strdup(word);
+
+ checkextra();
+}
+
+static void
+parse_performer(track_t trackp[], state_t *sp)
+{
+ char *word;
+ textptr_t *txp;
+
+ word = needitem();
+ txp = gettextptr(sp->track, trackp);
+ txp->tc_performer = strdup(word);
+
+ checkextra();
+}
+
+static void
+parse_postgap(track_t trackp[], state_t *sp)
+{
+ long l;
+
+ if (sp->state < STATE_INDEX1)
+ cueabort("Badly placed POSTGAP keyword");
+ sp->state = STATE_POSTGAP;
+
+ parse_offset(&l);
+ sp->postgapsize = l;
+
+ checkextra();
+}
+
+static void
+parse_pregap(track_t trackp[], state_t *sp)
+{
+ long l;
+
+ if ((sp->state < STATE_TRACK) ||
+ (sp->state >= STATE_INDEX0))
+ cueabort("Badly placed PREGAP keyword");
+ sp->state = STATE_FLAGS;
+
+ parse_offset(&l);
+ sp->pregapsize = l;
+
+ checkextra();
+}
+
+static void
+parse_songwriter(track_t trackp[], state_t *sp)
+{
+ char *word;
+ textptr_t *txp;
+
+ word = needitem();
+ txp = gettextptr(sp->track, trackp);
+ txp->tc_songwriter = strdup(word);
+
+ checkextra();
+}
+
+static void
+parse_title(track_t trackp[], state_t *sp)
+{
+ char *word;
+ textptr_t *txp;
+
+ word = needitem();
+ txp = gettextptr(sp->track, trackp);
+ txp->tc_title = strdup(word);
+
+ checkextra();
+}
+
+static void
+parse_track(track_t trackp[], state_t *sp)
+{
+ struct keyw *kp;
+ char *word;
+ long l;
+ long secsize = -1;
+
+ if ((sp->state >= STATE_TRACK) &&
+ (sp->state < STATE_INDEX1))
+ cueabort("Badly placed TRACK keyword");
+ sp->state = STATE_TRACK;
+ sp->index = -1;
+
+ word = needitem();
+ if (*astolb(word, &l, 10) != '\0')
+ cueabort("Not a number '%s'", word);
+ if (l <= 0 || l > 99)
+ cueabort("Illegal TRACK number '%s'", word);
+
+ if ((sp->track < l) &&
+ (((sp->track + 1) == l) || sp->track == 0))
+ sp->track = l;
+ else
+ cueabort("Badly placed TRACK %ld number", l);
+
+ word = needword();
+ kp = lookup(word, dtypes);
+ if (kp == NULL)
+ cueabort("Unknown filetype '%s'", word);
+
+ if (wordendc == '/') {
+ word = needitem();
+ if (*astol(++word, &secsize) != '\0')
+ cueabort("Not a number '%s'", word);
+ }
+
+ /*
+ * Reset all flags that may be set in TRACK & FLAGS lines
+ */
+ sp->flags &= ~(TI_AUDIO|TI_COPY|TI_QUADRO|TI_PREEMP|TI_SCMS);
+
+ if (kp->k_type == K_AUDIO)
+ sp->flags |= TI_AUDIO;
+
+ switch (kp->k_type) {
+
+ case K_CDG:
+ if (secsize < 0)
+ secsize = 2448;
+ case K_AUDIO:
+ if (secsize < 0)
+ secsize = 2352;
+
+ sp->tracktype = TOC_DA;
+ sp->sectype = SECT_AUDIO;
+ sp->dbtype = DB_RAW;
+ sp->secsize = secsize;
+ sp->dataoff = 0;
+ if (secsize != 2352)
+ cueabort("Unsupported sector size %ld for audio", secsize);
+ break;
+
+ case K_MODE1:
+ if (secsize < 0)
+ secsize = 2048;
+
+ sp->tracktype = TOC_ROM;
+ sp->sectype = SECT_ROM;
+ sp->dbtype = DB_ROM_MODE1;
+ sp->secsize = secsize;
+ sp->dataoff = 16;
+ /*
+ * XXX Sector Size == 2352 ???
+ * XXX It seems that there exist bin/cue pairs with this value
+ */
+ if (secsize != 2048)
+ cueabort("Unsupported sector size %ld for data", secsize);
+ break;
+
+ case K_MODE2:
+ case K_CDI:
+ sp->tracktype = TOC_ROM;
+ sp->sectype = SECT_MODE_2;
+ sp->dbtype = DB_ROM_MODE2;
+ sp->secsize = secsize;
+ sp->dataoff = 16;
+ if (secsize == 2352) {
+ sp->tracktype = TOC_XA2;
+ sp->sectype = SECT_MODE_2_MIX;
+ sp->sectype |= ST_MODE_RAW;
+ sp->dbtype = DB_RAW;
+ sp->dataoff = 0;
+ } else if (secsize != 2336)
+ cueabort("Unsupported sector size %ld for mode2", secsize);
+ if (kp->k_type == K_CDI)
+ sp->tracktype = TOC_CDI;
+ break;
+
+ default: cueabort("Panic: unknown datatype '%s'", word);
+ }
+
+ if (sp->flags & TI_PREEMP)
+ sp->sectype |= ST_PREEMPMASK;
+ sp->secsize = secsize;
+
+ if (xdebug > 1) {
+ printf("Track %d Tracktype %s/%d\n",
+ sp->track, kp->k_name, sp->secsize);
+ }
+
+ checkextra();
+}
+
+static void
+parse_offset(long *lp)
+{
+ char *word;
+ char *p;
+ long m = -1;
+ long s = -1;
+ long f = -1;
+
+ word = needitem();
+
+ if (strchr(word, ':') == NULL) {
+ if (*astol(word, lp) != '\0')
+ cueabort("Not a number '%s'", word);
+ return;
+ }
+ if (*(p = astolb(word, &m, 10)) != ':')
+ cueabort("Not a number '%s'", word);
+ if (m < 0 || m >= 160)
+ cueabort("Illegal minute value in '%s'", word);
+ p++;
+ if (*(p = astolb(p, &s, 10)) != ':')
+ cueabort("Not a number '%s'", p);
+ if (s < 0 || s >= 60)
+ cueabort("Illegal second value in '%s'", word);
+ p++;
+ if (*(p = astolb(p, &f, 10)) != '\0')
+ cueabort("Not a number '%s'", p);
+ if (f < 0 || f >= 75)
+ cueabort("Illegal frame value in '%s'", word);
+
+ m = m * 60 + s;
+ m = m * 75 + f;
+ *lp = m;
+}
+
+/*--------------------------------------------------------------------------*/
+static void
+newtrack(track_t trackp[], state_t *sp)
+{
+ register int i;
+ register int track = sp->track;
+ Llong tracksize;
+
+ if (xdebug > 1)
+ printf("-->Newtrack %d\n", track);
+ if (track > 1) {
+ tracksize = (sp->index1 - sp->secoff) * trackp[track-1].secsize;
+
+ if (xdebug > 1)
+ printf(" trackoff %lld filesize %lld index1 %ld size %ld/%lld\n",
+ sp->trackoff, sp->filesize, sp->index1,
+ sp->index1 - sp->secoff,
+ tracksize);
+
+ trackp[track-1].itracksize = tracksize;
+ trackp[track-1].tracksize = tracksize;
+ trackp[track-1].tracksecs = sp->index1 - sp->secoff;
+
+ sp->trackoff += tracksize;
+ sp->secoff = sp->index1;
+ }
+ /*
+ * Make 'tracks' immediately usable in track structure.
+ */
+ for (i = 0; i < MAX_TRACK+2; i++)
+ trackp[i].tracks = track;
+
+ trackp[track].filename = sp->filename;
+ trackp[track].xfp = xopen(sp->filename, O_RDONLY|O_BINARY, 0);
+ trackp[track].trackstart = 0L;
+/*
+SEtzen wenn tracksecs bekannt sind
+d.h. mit Index0 oder Index 1 vom nächsten track
+
+ trackp[track].itracksize = tracksize;
+ trackp[track].tracksize = tracksize;
+ trackp[track].tracksecs = -1L;
+*/
+ tracksize = sp->filesize - sp->trackoff;
+
+ trackp[track].itracksize = tracksize;
+ trackp[track].tracksize = tracksize;
+ trackp[track].tracksecs = (tracksize + sp->secsize - 1) / sp->secsize;
+
+ if (xdebug > 1)
+ printf(" Remaining Filesize %lld (%lld secs)\n",
+ (sp->filesize-sp->trackoff),
+ (sp->filesize-sp->trackoff +sp->secsize - 1) / sp->secsize);
+
+ if (sp->pregapsize >= 0) {
+/* trackp[track].flags &= ~TI_PREGAP;*/
+ sp->flags &= ~TI_PREGAP;
+ trackp[track].pregapsize = sp->pregapsize;
+ } else {
+/* trackp[track].flags |= TI_PREGAP;*/
+ if (track > 1)
+ sp->flags |= TI_PREGAP;
+ if (track == 1)
+ trackp[track].pregapsize = sp->index1 + 150;
+ else if (sp->index0 < 0)
+ trackp[track].pregapsize = -1;
+ else
+ trackp[track].pregapsize = sp->index1 - sp->index0;
+ }
+/* trackp[track].padsecs = xxx*/
+
+ trackp[track].isecsize = sp->secsize;
+ trackp[track].secsize = sp->secsize;
+ trackp[track].flags = sp->flags | trackp[0].flags;
+
+ trackp[track].secspt = 0; /* transfer size is set up in set_trsizes() */
+/* trackp[track].pktsize = pktsize; */
+ trackp[track].pktsize = 0;
+ trackp[track].trackno = sp->track;
+ trackp[track].sectype = sp->sectype;
+
+ trackp[track].dataoff = sp->dataoff;
+ trackp[track].tracktype = sp->tracktype;
+ trackp[track].dbtype = sp->dbtype;
+
+ if (track == 1) {
+ trackp[0].tracktype &= ~TOC_MASK;
+ trackp[0].tracktype |= sp->tracktype;
+
+ if (xdebug > 1) {
+ printf("Track %d Tracktype %X\n",
+ 0, trackp[0].tracktype);
+ }
+ }
+ if (xdebug > 1) {
+ printf("Track %d Tracktype %X\n",
+ track, trackp[track].tracktype);
+ }
+ trackp[track].nindex = 1;
+ trackp[track].tindex = 0;
+
+ if (xdebug > 1) {
+ printf("Track %d flags 0x%08X\n", 0, trackp[0].flags);
+ printf("Track %d flags 0x%08X\n", track, trackp[track].flags);
+ }
+}
+
+/*--------------------------------------------------------------------------*/
+static keyw_t *
+lookup(char *word, keyw_t table[])
+{
+ register keyw_t *kp = table;
+
+ while (kp->k_name) {
+ if (streql(kp->k_name, word))
+ return (kp);
+ kp++;
+ }
+ return (NULL);
+}
+
+/*--------------------------------------------------------------------------*/
+/*
+ * Parser low level functions start here...
+ */
+
+static void
+wdebug()
+{
+/* printf("WORD: '%s' rest '%s'\n", word, peekword());*/
+ printf("WORD: '%s' rest '%s'\n", linep, peekword());
+ printf("linep %lX peekword %lX end %lX\n",
+ (long)linep, (long)peekword(), (long)&linebuf[linelen]);
+}
+
+static FILE *
+cueopen(char *name)
+{
+ FILE *f;
+
+ f = fileopen(name, "r");
+ if (f == NULL)
+ comerr("Cannot open '%s'.\n", name);
+
+ fname = name;
+ return (f);
+}
+
+static char *
+cuename()
+{
+ return (fname);
+}
+
+static char *
+nextline(FILE *f)
+{
+ register int len;
+
+ do {
+ fillbytes(linebuf, sizeof (linebuf), '\0');
+ len = rols_fgetline(f, linebuf, sizeof (linebuf));
+ if (len < 0)
+ return (NULL);
+ if (len > 0 && linebuf[len-1] == '\r') {
+ linebuf[len-1] = '\0';
+ len--;
+ }
+ linelen = len;
+ lineno++;
+ } while (linebuf[0] == '#');
+
+ olinelen = linelen;
+ linep = linebuf;
+ wordendp = linep;
+ wordendc = *linep;
+
+ return (linep);
+}
+
+static void
+ungetline()
+{
+ linelen = olinelen;
+ linep = linebuf;
+ *wordendp = wordendc;
+ wordendp = linep;
+ wordendc = *linep;
+}
+
+static char *
+skipwhite(const char *s)
+{
+ register const Uchar *p = (const Uchar *)s;
+
+ while (*p) {
+ if (!isspace(*p))
+ break;
+ p++;
+ }
+ return ((char *)p);
+}
+
+static char *
+peekword()
+{
+ return (&wordendp[1]);
+}
+
+static char *
+lineend()
+{
+ return (&linebuf[linelen]);
+}
+
+static char *
+markword(char *delim)
+{
+ register BOOL quoted = FALSE;
+ register Uchar c;
+ register Uchar *s;
+ register Uchar *from;
+ register Uchar *to;
+
+ for (s = (Uchar *)linep; (c = *s) != '\0'; s++) {
+ if (c == '"') {
+ quoted = !quoted;
+/* strcpy((char *)s, (char *)&s[1]);*/
+ for (to = s, from = &s[1]; *from; ) {
+ c = *from++;
+ if (c == '\\' && quoted && (*from == '\\' || *from == '"'))
+ c = *from++;
+ *to++ = c;
+ }
+ *to = '\0';
+ c = *s;
+linelen--;
+ }
+ if (!quoted && isspace(c))
+ break;
+ if (!quoted && strchr(delim, c) && s > (Uchar *)linep)
+ break;
+ }
+ wordendp = (char *)s;
+ wordendc = (char)*s;
+ *s = '\0';
+
+ return (linep);
+}
+
+static char *
+getnextitem(char *delim)
+{
+ *wordendp = wordendc;
+
+ linep = skipwhite(wordendp);
+ return (markword(delim));
+}
+
+static char *
+neednextitem(char *delim)
+{
+ char *olinep = linep;
+ char *nlinep;
+
+ nlinep = getnextitem(delim);
+
+ if ((olinep == nlinep) || (*nlinep == '\0'))
+ cueabort("Missing text");
+
+ return (nlinep);
+}
+
+static char *
+nextword()
+{
+ return (getnextitem(worddelim));
+}
+
+static char *
+needword()
+{
+ return (neednextitem(worddelim));
+}
+
+static char *
+curword()
+{
+ return (linep);
+}
+
+static char *
+nextitem()
+{
+ return (getnextitem(nulldelim));
+}
+
+static char *
+needitem()
+{
+ return (neednextitem(nulldelim));
+}
+
+static void
+checkextra()
+{
+ if (peekword() < lineend())
+ cueabort("Extra text '%s'", peekword());
+}
+
+/* VARARGS1 */
+static void cueabort(const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, " on line %d in '%s'.\n", lineno, fname);
+ exit(EXIT_FAILURE);
+}
diff --git a/wodim/defaults.c b/wodim/defaults.c
new file mode 100644
index 0000000..a09da6c
--- /dev/null
+++ b/wodim/defaults.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2006 Eduard Bloch
+ *
+ * This code emulates the interface of the original defaults.c file. However,
+ * it improves its behaviour and deals with corner cases: prepended and
+ * trailing spaces on variable and value, no requirement for using TABs
+ * anymore. No requirements to insert dummy values like -1 or "".
+ *
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <deflts.h>
+#include <ctype.h>
+#include <string.h>
+
+#define CFGPATH "/etc/wodim.conf"
+/* The better way would be exporting the meta functions to getnum.h or so */
+extern int getnum(char *arg, long *valp);
+
+void
+cdr_defaults(char **p_dev_name, int *p_speed, long *p_fifosize,
+ char **p_drv_opts)
+{
+ char *t; /* tmp */
+ int wc=0;
+ char loc[256], sSpeed[11], sFs[11], sOpts[81];
+ char *devcand=NULL;
+
+ cfg_open(CFGPATH);
+
+ if(p_dev_name && *p_dev_name)
+ devcand=*p_dev_name;
+ else if(NULL!=(t=getenv("CDR_DEVICE")))
+ devcand=t;
+ else if(NULL!=(t=cfg_get("CDR_DEVICE")))
+ devcand=strdup(t); /* needs to use it as a key later, same stat. memory */
+
+ if(devcand && NULL != (t=cfg_get(devcand))) {
+ /* extract them now, may be used later */
+ wc=sscanf(t, "%255s %10s %10s %80s", loc, sSpeed, sFs, sOpts);
+ }
+
+ if(p_dev_name) {
+ if(wc>0)
+ *p_dev_name = strdup(loc);
+ else if(devcand) /* small mem. leak possible, does not matter, checks for that would require more code size than we loose */
+ *p_dev_name=strdup(devcand);
+ }
+ if(p_speed) { /* sth. to write back */
+ char *bad;
+ int cfg_speed=-1;
+
+ /* that value may be used twice */
+ if(NULL!=(t=cfg_get("CDR_SPEED"))) {
+ cfg_speed=strtol(t,&bad,10);
+ if(*bad || cfg_speed<-1) {
+ fprintf(stderr, "Bad default CDR_SPEED setting (%s).\n", t);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if(*p_speed>0) {
+ /* ok, already set by the program arguments */
+ }
+ else if(NULL!=(t=getenv("CDR_SPEED"))) {
+ *p_speed=strtol(t,&bad,10);
+ if(*bad || *p_speed<-1) {
+ fprintf(stderr, "Bad CDR_SPEED environment (%s).\n", t);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if(wc>1 && *sSpeed) {
+ *p_speed=strtol(sSpeed, &bad, 10);
+ if(*bad || *p_speed<-1) {
+ fprintf(stderr, "Bad speed (%s) in the config, drive description.\n", sSpeed);
+ exit(EXIT_FAILURE);
+ }
+ if(*p_speed==-1)
+ /* that's autodetect, use the config default as last ressort */
+ *p_speed=cfg_speed;
+ }
+ else
+ *p_speed=cfg_speed;
+ }
+ if(p_fifosize) { /* sth. to write back */
+ if(*p_fifosize>0) {
+ /* ok, already set by the user */
+ }
+ else if(NULL!=(t=getenv("CDR_FIFOSIZE"))) {
+ if(getnum(t, p_fifosize)!=1 || *p_fifosize<-1) {
+ fprintf(stderr, "Bad CDR_FIFOSIZE environment (%s).\n", t);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if(wc>2 && *sFs && strcmp("-1", sFs)) {
+ if(getnum(sFs, p_fifosize)!=1 || *p_fifosize<-1) {
+ fprintf(stderr, "Bad fifo size (%s) in the config, device description.\n", sFs);
+ exit(EXIT_FAILURE);
+ }
+ }
+ else if(NULL!=(t=cfg_get("CDR_FIFOSIZE"))) {
+ if(getnum(t, p_fifosize)!=1 || *p_fifosize<-1) {
+ fprintf(stderr, "Bad speed default setting (%s).\n", t);
+ exit(EXIT_FAILURE);
+ }
+ }
+ if(NULL!=(t=cfg_get("CDR_MAXFIFOSIZE"))) {
+ long max;
+ if(getnum(t, &max)!=1 || *p_fifosize<-1) {
+ fprintf(stderr, "Bad CDR_MAXFIFOSIZE setting (%s).\n", t);
+ exit(EXIT_FAILURE);
+ }
+ if(*p_fifosize>max)
+ *p_fifosize=max;
+ }
+ }
+
+ if(p_drv_opts && !*p_drv_opts && wc>3 && strcmp(sOpts, "\"\""))
+ *p_drv_opts=strdup(sOpts);
+
+ cfg_close();
+
+}
diff --git a/wodim/defaults.h b/wodim/defaults.h
new file mode 100644
index 0000000..3a5c8f8
--- /dev/null
+++ b/wodim/defaults.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2006 Eduard Bloch
+ *
+ * This code emulates the interface of the original defaults.c file. However,
+ * it improves its behaviour and deals with corner cases: prepended and
+ * trailing spaces on variable and value, no requirement for using TABs
+ * anymore. No requirements to insert dummy values like -1 or "".
+ *
+ */
+#ifndef _DEFAULTS_H_
+#define _DEFAULTS_H_
+extern int getnum(char *arg, long *valp);
+void cdr_defaults(char **p_dev_name, int *p_speed, long *p_fifosize, char **p_drv_opts);
+#endif
diff --git a/wodim/diskid.c b/wodim/diskid.c
new file mode 100644
index 0000000..70675a3
--- /dev/null
+++ b/wodim/diskid.c
@@ -0,0 +1,522 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)diskid.c 1.37 04/03/02 Copyright 1998-2004 J. Schilling */
+/*
+ * Disk Idientification Method
+ *
+ * Copyright (c) 1998-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <utypes.h>
+
+#include "wodim.h"
+
+void pr_manufacturer(msf_t *mp, BOOL rw, BOOL audio);
+static struct disk_man *man_ptr(msf_t *mp);
+int manufacturer_id(msf_t *mp);
+long disk_rcap(msf_t *mp, long maxblock, BOOL rw, BOOL audio);
+
+struct disk_man {
+ msf_t mi_msf;
+ char mi_num;
+ char *mi_name;
+};
+
+/*
+ * Illegal (old) Manufacturer.
+ */
+static char m_ill[] = "Unknown old Manufacturer code";
+static char m_illrw[] = "Illegal Manufacturer code";
+
+/*
+ * Permanent codes.
+ */
+static char m_kingpro[] = "King Pro Mediatek Inc.";
+static char m_custpo[] = "Customer Pressing Oosterhout";
+static char m_taeil[] = "Taeil Media Co.,Ltd.";
+static char m_doremi[] = "Doremi Media Co., Ltd.";
+static char m_xcitec[] = "Xcitec Inc.";
+static char m_leaddata[] = "Lead Data Inc.";
+static char m_fuji[] = "FUJI Photo Film Co., Ltd.";
+static char m_hitachi[] = "Hitachi Maxell, Ltd.";
+static char m_kodakjp[] = "Kodak Japan Limited";
+static char m_mitsui[] = "Mitsui Chemicals, Inc.";
+static char m_pioneer[] = "Pioneer Video Corporation";
+static char m_plasmon[] = "Plasmon Data systems Ltd.";
+static char m_princo[] = "Princo Corporation";
+static char m_ricoh[] = "Ricoh Company Limited";
+static char m_skc[] = "SKC Co., Ltd.";
+static char m_tyuden[] = "Taiyo Yuden Company Limited";
+static char m_tdk[] = "TDK Corporation";
+static char m_mitsubishi[] = "Mitsubishi Chemical Corporation";
+static char m_auvistar[] = "Auvistar Industry Co.,Ltd.";
+static char m_gigastore[] = "GIGASTORAGE CORPORATION";
+static char m_fornet[] = "FORNET INTERNATIONAL PTE LTD.";
+static char m_cmc[] = "CMC Magnetics Corporation";
+static char m_odm[] = "Optical Disc Manufacturing Equipment";
+static char m_ritek[] = "Ritek Co.";
+
+/*
+ * Tentative codes.
+ */
+static char m_bestdisk[] = "Bestdisc Technology Corporation";
+static char m_wealth_fair[] = "WEALTH FAIR INVESTMENT LIMITED";
+static char m_general_mag[] = "General Magnetics Ld";
+static char m_mpo[] = "MPO";
+static char m_jvc[] = "VICTOR COMPANY OF JAPAN, LIMITED";
+static char m_vivistar[] = "VIVASTAR AG";
+static char m_taroko[] = "TAROKO INTERNATIONAL CO.,LTD.";
+static char m_unidisc[] = "UNIDISC TECHNOLOGY CO.,LTD";
+static char m_hokodig[] = "Hong Kong Digital Technology Co., Ltd.";
+static char m_viva[] = "VIVA MAGNETICS LIMITED";
+static char m_hile[] = "Hile Optical Disc Technology Corp.";
+static char m_friendly[] = "Friendly CD-Tek Co.";
+static char m_soundsound[] = "Sound Sound Multi-Media Development Limited";
+static char m_kdg[] = "kdg mediatech AG";
+static char m_seantram[] = "Seantram Technology Inc.";
+static char m_eximpo[] = "EXIMPO";
+static char m_delphi[] = "DELPHI TECHNOLOGY INC.";
+static char m_harmonic[] = "Harmonic Hall Optical Disc Ltd.";
+static char m_guannyinn[] = "Guann Yinn Co.,Ltd.";
+static char m_optime[] = "Opti.Me.S. S.p.A.";
+static char m_nacar[] = "Nacar Media srl";
+static char m_optrom[] = "OPTROM.INC.";
+static char m_audiodis[] = "AUDIO DISTRIBUTORS CO., LTD.";
+static char m_acer[] = "Acer Media Technology, Inc.";
+static char m_woongjin[] = "Woongjin Media corp";
+static char m_infodisk[] = "INFODISC Technology Co., Ltd.";
+static char m_unitech[] = "UNITECH JAPAN INC.";
+static char m_ams[] = "AMS Technology Inc.";
+static char m_vanguard[] = "Vanguard Disc Inc.";
+static char m_grandadv[] = "Grand Advance Technology Ltd.";
+static char m_digitalstor[] = "DIGITAL STORAGE TECHNOLOGY CO.,LTD";
+static char m_matsushita[] = "Matsushita Electric Industrial Co.,Ltd.";
+static char m_albrechts[] = "CDA Datenträger Albrechts GmbH.";
+static char m_xalbrechts[] = "??? CDA Datenträger Albrechts GmbH.";
+
+static char m_prodisc[] = "Prodisc Technology Inc.";
+static char m_postech[] = "POSTECH Corporation";
+#ifdef used
+static char m_ncolumbia[] = "NIPPON COLUMBIA CO.,LTD.";
+#endif
+static char m_odc[] = "OPTICAL DISC CORPRATION";
+static char m_sony[] = "SONY Corporation";
+static char m_cis[] = "CIS Technology Inc.";
+static char m_csitaly[] = "Computer Support Italy s.r.l.";
+static char m_mmmm[] = "Multi Media Masters & Machinary SA";
+
+/*
+ * Guessed codes.
+ */
+/*static char m_seantram[] = "Seantram Technology Inc.";*/
+static char m_advanced[] = "Advanced Digital Media";
+static char m_moser[] = "Moser Baer India Limited";
+static char m_nanya[] = "NAN-YA Plastics Corporation";
+static char m_shenzen[] = "SHENZEN SG&GAST DIGITAL OPTICAL DISCS";
+
+static struct disk_man notable =
+ {{00, 00, 00}, -1, "unknown (not in table)" };
+
+/*
+ * Old (illegal) code table. It lists single specific codes (97:xx:yy).
+ */
+static struct disk_man odman[] = {
+ /*
+ * Illegal (old) codes.
+ */
+ {{97, 25, 00}, 80, "ILLEGAL OLD CODE: TDK ???" },
+ {{97, 25, 15}, 0, m_ill },
+ {{97, 27, 00}, 81, "ILLEGAL OLD CODE: Old Ritek Co.???" },
+ {{97, 27, 25}, 0, m_ill },
+ {{97, 30, 00}, 0, m_ill },
+ {{97, 33, 00}, 82, "ILLEGAL OLD CODE: Old CDA Datenträger Albrechts GmbH." },
+ {{97, 35, 44}, 0, m_ill },
+ {{97, 39, 00}, 0, m_ill },
+ {{97, 45, 36}, 83, "ILLEGAL OLD CODE: Old Kodak Photo CD" },
+ {{97, 47, 00}, 0, m_ill },
+ {{97, 47, 30}, 0, m_ill },
+ {{97, 48, 14}, 0, m_ill },
+ {{97, 48, 33}, 0, m_ill },
+ {{97, 49, 00}, 0, m_ill },
+ {{97, 54, 00}, 0, m_ill },
+ {{97, 55, 06}, 0, m_ill },
+ {{97, 57, 00}, 0, m_ill },
+ /*
+ * List end marker
+ */
+ {{00, 00, 00}, 0, NULL },
+};
+
+#define noman (sizeof (oman)/sizeof (oman[0]))
+
+/*
+ * Actual code table. It lists code ranges (97:xx:y0 - 97:xx:y9).
+ *
+ * Note that dp->mi_msf.msf_frame needs to be always rounded down
+ * to 0 even for media that has e.g. 97:27/01 in the official table.
+ */
+static struct disk_man dman[] = {
+ /*
+ * Permanent codes.
+ */
+
+ {{97, 22, 10}, 53, m_seantram },
+ {{97, 15, 00}, 26, m_tdk },
+ {{97, 49, 30}, 47, m_optime },
+ {{97, 28, 00}, 47, m_optime },
+ {{97, 28, 40}, 36, m_kingpro },
+ {{97, 23, 60}, 49, m_custpo },
+ {{97, 29, 00}, 37, m_taeil },
+ {{97, 26, 10}, 19, m_postech },
+ {{97, 47, 40}, 19, m_postech },
+ {{97, 24, 10}, 24, m_sony },
+/* {{97, 46, 10}, 24, m_sony },*/
+ {{97, 23, 10}, 33, m_doremi },
+ {{97, 25, 60}, 30, m_xcitec },
+ {{97, 45, 60}, 30, m_xcitec },
+ {{97, 26, 50}, 10, m_leaddata },
+ {{97, 48, 60}, 10, m_leaddata },
+ {{97, 26, 40}, 6, m_fuji },
+ {{97, 46, 40}, 6, m_fuji },
+ {{97, 25, 20}, 8, m_hitachi },
+ {{97, 47, 10}, 8, m_hitachi },
+ {{97, 27, 40}, 9, m_kodakjp },
+ {{97, 48, 10}, 9, m_kodakjp },
+ {{97, 27, 50}, 12, m_mitsui },
+ {{97, 48, 50}, 12, m_mitsui },
+ {{97, 27, 30}, 17, m_pioneer },
+ {{97, 48, 30}, 17, m_pioneer },
+ {{97, 27, 10}, 18, m_plasmon },
+ {{97, 48, 20}, 18, m_plasmon },
+ {{97, 27, 20}, 20, m_princo },
+ {{97, 47, 20}, 20, m_princo },
+ {{97, 27, 60}, 21, m_ricoh },
+ {{97, 48, 00}, 21, m_ricoh },
+ {{97, 26, 20}, 23, m_skc },
+ {{97, 24, 00}, 25, m_tyuden },
+ {{97, 46, 00}, 25, m_tyuden },
+ {{97, 32, 00}, 26, m_tdk },
+ {{97, 49, 00}, 26, m_tdk },
+ {{97, 34, 20}, 11, m_mitsubishi },
+ {{97, 50, 20}, 11, m_mitsubishi },
+ {{97, 28, 30}, 1, m_auvistar },
+ {{97, 46, 50}, 1, m_auvistar },
+ {{97, 28, 10}, 7, m_gigastore },
+ {{97, 49, 10}, 7, m_gigastore },
+ {{97, 26, 00}, 5, m_fornet },
+ {{97, 45, 00}, 5, m_fornet },
+ {{97, 26, 60}, 3, m_cmc },
+ {{97, 46, 60}, 3, m_cmc },
+ {{97, 21, 40}, 16, m_odm },
+ {{97, 31, 00}, 22, m_ritek },
+ {{97, 47, 50}, 22, m_ritek },
+ {{97, 28, 20}, 13, m_mmmm },
+ {{97, 46, 20}, 13, m_mmmm },
+ {{97, 32, 10}, 27, m_prodisc },
+
+ /*
+ * Tentative codes.
+ */
+ {{97, 21, 30}, 67, m_bestdisk },
+ {{97, 18, 10}, 66, m_wealth_fair },
+ {{97, 29, 50}, 65, m_general_mag },
+ {{97, 25, 00}, 64, m_mpo }, /* in reality 25/01 */
+ {{97, 49, 40}, 63, m_jvc },
+ {{97, 23, 40}, 63, m_jvc },
+ {{97, 25, 40}, 62, m_vivistar },
+ {{97, 18, 60}, 61, m_taroko },
+ {{97, 29, 20}, 60, m_unidisc },
+ {{97, 46, 10}, 59, m_hokodig }, /* XXX was m_sony */
+ {{97, 22, 50}, 59, m_hokodig },
+ {{97, 29, 40}, 58, m_viva },
+ {{97, 29, 30}, 57, m_hile },
+ {{97, 51, 50}, 57, m_hile },
+ {{97, 28, 60}, 56, m_friendly },
+ {{97, 21, 50}, 55, m_soundsound },
+ {{97, 24, 40}, 54, m_kdg },
+ {{97, 22, 30}, 52, m_eximpo },
+ {{97, 28, 50}, 51, m_delphi },
+ {{97, 29, 00}, 50, m_harmonic },
+ {{97, 15, 10}, 22, m_ritek },
+ {{97, 45, 50}, 48, m_guannyinn },
+ {{97, 24, 50}, 48, m_guannyinn },
+ {{97, 23, 20}, 46, m_nacar },
+ {{97, 23, 50}, 45, m_optrom },
+ {{97, 23, 30}, 44, m_audiodis },
+ {{97, 22, 60}, 43, m_acer },
+ {{97, 45, 20}, 43, m_acer },
+ {{97, 15, 20}, 11, m_mitsubishi },
+ {{97, 22, 00}, 39, m_woongjin },
+ {{97, 25, 30}, 40, m_infodisk },
+ {{97, 51, 20}, 40, m_infodisk },
+ {{97, 24, 30}, 41, m_unitech },
+ {{97, 25, 50}, 42, m_ams },
+ {{97, 29, 10}, 38, m_vanguard },
+ {{97, 50, 10}, 38, m_vanguard },
+ {{97, 16, 30}, 35, m_grandadv },
+ {{97, 31, 30}, 35, m_grandadv },
+ {{97, 51, 10}, 35, m_grandadv },
+ {{97, 49, 20}, 36, m_kingpro },
+ {{97, 27, 00}, 34, m_digitalstor }, /* in reality 27/01 */
+ {{97, 48, 40}, 34, m_digitalstor }, /* XXX was m_ncolumbia */
+ {{97, 23, 00}, 31, m_matsushita },
+ {{97, 49, 60}, 31, m_matsushita },
+ {{97, 30, 10}, 32, m_albrechts }, /* XXX was m_ncolumbia */
+ {{97, 50, 30}, 32, m_albrechts },
+ {{97, 47, 60}, 27, m_prodisc },
+/* {{97, 30, 10}, 14, m_ncolumbia },*/
+/* {{97, 48, 40}, 14, m_ncolumbia },*/
+ {{97, 26, 30}, 15, m_odc },
+ {{97, 22, 40}, 2, m_cis },
+ {{97, 45, 40}, 2, m_cis },
+ {{97, 24, 20}, 4, m_csitaly },
+ {{97, 46, 30}, 4, m_csitaly },
+
+ /*
+ * Guessed codes.
+ */
+ {{97, 20, 10}, 32, m_xalbrechts }, /* XXX guess */
+/* {{97, 23, 40}, 32, m_xalbrechts },*/ /* Really is JVC */
+
+ /*
+ * New guessed codes (2002 ff.).
+ * Id code >= 68 referres to a new manufacturer.
+ */
+#define I_GUESS 105
+ {{97, 22, 20}, 68, m_advanced },
+ {{97, 42, 20}, 68, m_advanced },
+ {{97, 24, 60}, 50, m_harmonic },
+ {{97, 17, 00}, 69, m_moser },
+ {{97, 15, 30}, 70, m_nanya },
+ {{97, 16, 20}, 71, m_shenzen },
+ {{97, 45, 10}, 41, m_unitech },
+
+ /*
+ * List end marker
+ */
+ {{00, 00, 00}, 0, NULL },
+};
+
+#define ndman (sizeof (dman)/sizeof (dman[0]))
+
+static struct disk_man *
+man_ptr(msf_t *mp)
+{
+ struct disk_man * dp;
+ int frame;
+ int type;
+
+ type = mp->msf_frame % 10;
+ frame = mp->msf_frame - type;
+
+ dp = odman;
+ while (dp->mi_msf.msf_min != 0) {
+ if (mp->msf_min == dp->mi_msf.msf_min &&
+ mp->msf_sec == dp->mi_msf.msf_sec &&
+ mp->msf_frame == dp->mi_msf.msf_frame) {
+ return (dp);
+ }
+ dp++;
+ }
+ dp = dman;
+ while (dp->mi_msf.msf_min != 0) {
+ if (mp->msf_min == dp->mi_msf.msf_min &&
+ mp->msf_sec == dp->mi_msf.msf_sec &&
+ frame == dp->mi_msf.msf_frame) {
+ /*
+ * Note that dp->mi_msf.msf_frame is always rounded
+ * down to 0 even for media that has 97:27/01 in the
+ * official table.
+ */
+ return (dp);
+ }
+ dp++;
+ }
+ return (NULL);
+}
+
+void pr_manufacturer(msf_t *mp, BOOL rw, BOOL audio)
+{
+ struct disk_man * dp;
+ struct disk_man xdman;
+ int type;
+ char *tname;
+
+/* printf("pr_manufacturer rw: %d audio: %d\n", rw, audio);*/
+
+ type = mp->msf_frame % 10;
+ if (type < 5) {
+ tname = "Long strategy type (Cyanine, AZO or similar)";
+ } else {
+ tname = "Short strategy type (Phthalocyanine or similar)";
+ }
+ if (rw) {
+ tname = "Phase change";
+ }
+
+ dp = man_ptr(mp);
+ if (dp != NULL) {
+ if (dp->mi_num == 0 || dp->mi_num >= 80) {
+ if (!rw) {
+ tname = "unknown dye (old id code)";
+ } else {
+ xdman = *dp;
+ dp = &xdman;
+ dp->mi_num = 0;
+ dp->mi_name = m_illrw;
+ }
+ }
+ } else {
+ tname = "unknown dye (reserved id code)";
+ dp = &notable;
+ }
+ printf("Disk type: %s\n", tname);
+ printf("Manuf. index: %d\n", dp->mi_num);
+ printf("Manufacturer: %s\n", dp->mi_name);
+
+ if (mp->msf_min != 97) /* This may be garbage ATIP from a DVD */
+ return;
+
+ if (dp >= &dman[I_GUESS] && dp < &dman[ndman]) {
+ printf("Manufacturer is guessed because of the orange forum embargo.\n");
+ printf("The orange forum likes to get money for recent information.\n");
+ printf("The information for this media may not be correct.\n");
+ }
+ if (dp == &notable) {
+ printf("Manufacturer is unknown because of the orange forum embargo.\n");
+ printf("As the orange forum likes to get money for recent information,\n");
+ printf("it may be that this media does not use illegal manufacturer coding.\n");
+ }
+}
+
+int manufacturer_id(msf_t *mp)
+{
+ struct disk_man * dp;
+
+ dp = man_ptr(mp);
+ if (dp != NULL)
+ return (dp->mi_num);
+ return (-1);
+}
+
+struct disk_rcap {
+ msf_t ci_msf; /* Lead in start time */
+ long ci_cap; /* Lead out start time */
+ long ci_rcap; /* Abs max lead out start */
+};
+
+static struct disk_rcap rcap[] = {
+
+#ifdef __redbook_only__
+ {{97, 35, 44}, 359849, 404700 }, /*! Unknown 99 min (89:58/00)*/
+#endif
+ {{97, 35, 44}, 359849, 449700 }, /*! Unknown 99 min (99:58/00) */
+ {{97, 31, 00}, 359849, 368923 }, /*! Arita CD-R 80 */
+ {{97, 26, 50}, 359849, 369096 }, /*! Lead Data CD-R 80 */
+ {{97, 26, 12}, 359849, 368000 }, /*X POSTECH 80 Min */
+ {{97, 25, 00}, 359849, 374002 }, /* TDK 80 Minuten */
+ {{97, 20, 14}, 359700, 376386 }, /*! Albrechts DataFile Plus */
+ {{97, 35, 44}, 359100, 368791 }, /*! NoName BC-1 700 Mb/80 Min */
+
+ {{97, 26, 60}, 337350, 349030 }, /* Koch grün CD-R74PRO */
+ {{97, 26, 50}, 337050, 351205 }, /* Saba */
+ {{97, 26, 00}, 337050, 351411 }, /*!DGN (FORNET) */
+ {{97, 22, 40}, 336631, 349971 }, /* Targa grün CD-R74 */
+ {{97, 26, 50}, 336631, 351727 }, /*! Sunstar (Lead Data) */
+ {{97, 26, 55}, 336631, 350474 }, /*! NoName ZAP (Lead Data) */
+
+ {{97, 27, 28}, 336601, 346489 }, /*! BTC CD-R (Princo) */
+ {{97, 27, 30}, 336601, 351646 }, /*! Pioneer blau CDM-W74S */
+ {{97, 27, 31}, 336601, 351379 }, /* Pioneer blau CDM-W74S */
+ {{97, 27, 33}, 336601, 347029 }, /*! Pioneer braun CDM-V74S */
+ {{97, 26, 40}, 336225, 346210 }, /* Fuji Silver Disk */
+ {{97, 28, 10}, 336225, 348757 }, /*!GigaStorage Cursor CD-R */
+ {{97, 31, 00}, 336225, 345460 }, /* Arita grün */
+ {{97, 25, 28}, 336075, 352879 }, /* Maxell gold CD-R74G */
+ {{97, 24, 01}, 336075, 346856 }, /*!Philips Premium Silver */
+ {{97, 24, 00}, 336075, 346741 }, /* Philips grün CD-R74 */
+
+ {{97, 22, 41}, 335206, 349385 }, /* Octek grün */
+ {{97, 34, 20}, 335100, 342460 }, /* Verbatim DataLifePlus */
+ {{97, 33, 00}, 335100, 344634 }, /*!ITS Singapore (braun/grün) */
+ {{97, 32, 19}, 335100, 343921 }, /*!Prodisc silber/silber */
+ {{97, 25, 21}, 335100, 346013 }, /* Maxell grün CD-R74XL */
+ {{97, 27, 00}, 335100, 353448 }, /* TDK grün CD-RXG74 */
+ {{97, 27, 31}, 335100, 351862 }, /*!Maxell CD-R74MU (Musik) */
+ {{97, 27, 33}, 335100, 351336 }, /* Pioneer RDD-74A */
+
+ {{97, 26, 60}, 334259, 349036 }, /* BASF grün */
+ {{97, 28, 21}, 333976, 348217 }, /*! Noname-B (MMMM) */
+ {{97, 28, 20}, 333976, 346485 }, /* Koch grün CD-R74 PRO */
+ {{97, 32, 00}, 333975, 345736 }, /* Imation 3M */
+ {{97, 32, 00}, 333975, 348835 }, /* TDK Reflex X CD-R74 */
+ {{97, 30, 18}, 333899, 344857 }, /* HiSpace grün */
+ {{97, 27, 66}, 333750, 352726 }, /*!Philips Megalife (Musik) */
+ {{97, 28, 43}, 333750, 345344 }, /*!MMore CD-R */
+ {{97, 27, 65}, 333750, 348343 }, /* Ricoh gold */
+
+ {{97, 27, 00}, 333750, 336246 }, /* BestMedia grün CD-R74 */
+ {{97, 27, 28}, 333491, 347473 }, /* Fuji grün (alt) */
+ {{97, 24, 48}, 333491, 343519 }, /* BASF (alt) */
+ {{97, 27, 55}, 333235, 343270 }, /* Teac gold CD-R74 */
+ {{97, 27, 45}, 333226, 343358 }, /* Kodak gold */
+ {{97, 28, 20}, 333226, 346483 }, /* SAST grün */
+ {{97, 27, 45}, 333226, 343357 }, /* Mitsumi gold */
+ {{97, 28, 25}, 333226, 346481 }, /* Cedar Grün */
+ {{97, 23, 00}, 333226, 346206 }, /* Fuji grün (alt) */
+ {{97, 33, 00}, 333225, 349623 }, /* DataFile Albrechts */
+ {{97, 24, 24}, 333198, 342536 }, /*!SUN CD Recordable */
+
+ {{97, 27, 19}, 332850, 348442 }, /* Plasmon gold PCD-R74 */
+ {{97, 32, 00}, 96600, 106502 }, /* TDK 80mm (for music only) */
+
+ /*
+ * List end marker
+ */
+ {{00, 00, 00}, 0L, 0L },
+};
+
+long
+disk_rcap(msf_t *mp, long maxblock, BOOL rw, BOOL audio)
+{
+ struct disk_rcap * dp;
+
+ dp = rcap;
+ while (dp->ci_msf.msf_min != 0) {
+ if (mp->msf_min == dp->ci_msf.msf_min &&
+ mp->msf_sec == dp->ci_msf.msf_sec &&
+ mp->msf_frame == dp->ci_msf.msf_frame &&
+ maxblock == dp->ci_cap)
+ return (dp->ci_rcap);
+ dp++;
+ }
+ return (0L);
+}
diff --git a/wodim/drv_7501.c b/wodim/drv_7501.c
new file mode 100644
index 0000000..5083750
--- /dev/null
+++ b/wodim/drv_7501.c
@@ -0,0 +1,1030 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)drv_7501.c 1.16 05/05/16 Copyright 2003-2005 J. Schilling */
+/*
+ * Device driver for the Masushita CW-7501
+ *
+ * Copyright (c) 2003-2005 J. Schilling
+ *
+ * Mode Pages:
+ * 0x01 error recovery Seite 100
+ * 0x02 disconnect/reconnect Seite 107
+ * 0x0D CD-ROM device parameter Seite 110
+ * 0x0E CD-ROM Audio control Seite 112
+ * 0x20 Speed & Tray position Seite 115
+ * 0x21 Media catalog number Seite 124
+ * 0x22 ISRC Seite 125
+ * 0x23 Dummy/Write Information Seite 126
+ * 0x24 CD-R disk information Seite 127
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include <libport.h>
+
+#include "wodim.h"
+
+extern int silent;
+extern int debug;
+extern int verbose;
+extern int lverbose;
+extern int xdebug;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct cw7501_mode_page_20 { /* Speed control */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar speed;
+ Ucbit res : 7;
+ Ucbit traypos : 1;
+};
+
+#else /* Motorola byteorder */
+
+struct cw7501_mode_page_20 { /* Speed control */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar speed;
+ Ucbit traypos : 1;
+ Ucbit res : 7;
+};
+#endif
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct cw7501_mode_page_21 { /* MCN */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x12 = 20 Bytes */
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar res_3;
+ Uchar res_4;
+ Uchar mcn[15];
+};
+
+#else /* Motorola byteorder */
+
+struct cw7501_mode_page_21 { /* MCN */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x12 = 20 Bytes */
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar res_3;
+ Uchar res_4;
+ Uchar mcn[15];
+};
+#endif
+
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct cw7501_mode_page_22 { /* ISRC */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x12 = 20 Bytes */
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar trackno;
+ Uchar res_4;
+ Uchar isrc[15];
+};
+
+#else /* Motorola byteorder */
+
+struct cw7501_mode_page_22 { /* ISRC */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x12 = 20 Bytes */
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar trackno;
+ Uchar res_4;
+ Uchar isrc[15];
+};
+#endif
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct cw7501_mode_page_23 { /* Dummy / Write information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar res;
+ Ucbit autopg : 1;
+ Ucbit dummy : 1;
+ Ucbit res3_72 : 6;
+};
+
+#else /* Motorola byteorder */
+
+struct cw7501_mode_page_23 { /* Dummy / Write information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar res;
+ Ucbit res3_72 : 6;
+ Ucbit dummy : 1;
+ Ucbit autopg : 1;
+};
+#endif
+
+struct cw7501_mode_page_24 { /* CD-R Disk information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x0A = 12 Bytes */
+ Uchar disktype;
+ Uchar res;
+ Uchar appl_code[4];
+ Uchar disk_id[4];
+};
+
+struct cw7501_mode_data {
+ struct scsi_mode_header header;
+ union cdd_pagex {
+ struct cw7501_mode_page_20 page20;
+ struct cw7501_mode_page_21 page21;
+ struct cw7501_mode_page_22 page22;
+ struct cw7501_mode_page_23 page23;
+ struct cw7501_mode_page_24 page24;
+ } pagex;
+};
+
+/*
+ * Mode for read track information
+ */
+#define TI_TRACKINFO_R 0
+#define TI_NWA 1
+#define TI_PMA 2
+#define TI_TRACKINFO 3
+
+struct cw7501_nwa {
+ Uchar nwa_length[2];
+ Uchar nwa_res;
+ Uchar nwa_trackno;
+ Uchar nwa_nwa[4];
+ Uchar nwa_freeblocks[4];
+};
+
+struct cw7501_cue {
+ Uchar cs_ctladr; /* CTL/ADR for this track */
+ Uchar cs_tno; /* This track number */
+ Uchar cs_index; /* Index within this track */
+ Uchar cs_dataform; /* Data form */
+ /* Bit 0..3 Physical Format */
+ /* Bit 4 Alt Copy (SCMS) */
+ /* Bit 5 SubC Audio + RAW96 sub */
+ Uchar cs_extension; /* Reserved or MCN/ISRC */
+ Uchar cs_min; /* Absolute time minutes */
+ Uchar cs_sec; /* Absolute time seconds */
+ Uchar cs_frame; /* Absolute time frames */
+};
+
+
+static int cw7501_attach(SCSI *usalp, cdr_t *dp);
+static int cw7501_init(SCSI *usalp, cdr_t *dp);
+static int cw7501_getdisktype(SCSI *usalp, cdr_t *dp);
+static int cw7501_speed_select(SCSI *usalp, cdr_t *dp, int *speedp);
+static int cw7501_next_wr_addr(SCSI *usalp, track_t *trackp, long *ap);
+static int cw7501_write(SCSI *usalp, caddr_t bp, long sectaddr, long size,
+ int blocks, BOOL islast);
+static int cw7501_write_leadin(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_open_track(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_close_track(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_open_session(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_gen_cue(track_t *trackp, void *vcuep, BOOL needgap);
+static void fillcue(struct cw7501_cue *cp, int ca, int tno, int idx,
+ int dataform, int scms, msf_t *mp);
+static int cw7501_send_cue(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_fixate(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int cw7501_rezero(SCSI *usalp, int reset, int dwreset);
+static int cw7501_read_trackinfo(SCSI *usalp, Uchar *bp, int count,
+ int track, int mode);
+static int cw7501_write_dao(SCSI *usalp, Uchar *bp, int len, int disktype);
+static int cw7501_reserve_track(SCSI *usalp, unsigned long);
+static int cw7501_set_mode(SCSI *usalp, int phys_form, int control,
+ int subc, int alt, int trackno, int tindex,
+ int packet_size, int write_mode);
+static int cw7501_finalize(SCSI *usalp, int pad, int fixed);
+
+
+cdr_t cdr_cw7501 = {
+ 0, 0,
+ /*
+ * Prinzipiell geht auch: CDR_PACKET & CDR_SRAW96R
+ */
+ CDR_TAO|CDR_SAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "cw_7501",
+ "driver for Matsushita/Panasonic CW-7501",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ cw7501_attach,
+ cw7501_init,
+ cw7501_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_dummy, /* RD buffer cap not supp. */
+ cmd_dummy, /* recovery_needed */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ cw7501_speed_select,
+ select_secsize,
+ cw7501_next_wr_addr,
+ cw7501_reserve_track,
+ cw7501_write,
+ cw7501_gen_cue,
+ cw7501_send_cue,
+ cw7501_write_leadin,
+ cw7501_open_track,
+ cw7501_close_track,
+ cw7501_open_session,
+ cmd_dummy, /* close seession */
+ cmd_dummy, /* abort */
+ read_session_offset,
+ cw7501_fixate,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+static const char *sd_cw7501_error_str[] = {
+ "\100\201diagnostic failure on ROM", /* 40 81 */
+ "\100\202diagnostic failure on CPU internal RAM", /* 40 82 */
+ "\100\203diagnostic failure on BUFFER RAM", /* 40 83 */
+ "\100\204diagnostic failure on internal SCSI controller", /* 40 84 */
+ "\100\205diagnostic failure on system mechanism", /* 40 85 */
+
+ "\210\000Illegal Que Sheet (DAO parameter)", /* 88 00 */
+ "\211\000Inappropriate command", /* 89 00 */
+
+ "\250\000Audio Play operation Not in Progress", /* A8 00 */
+ "\251\000Buffer Overrun", /* A9 00 */
+
+ "\300\000Unrecordable Disk", /* C0 00 */
+ "\301\000Illegal Track Status", /* C1 00 */
+ "\302\000Reserved track Status", /* C2 00 */
+ "\304\000Illegal Reserve Length for Reserve Track Command", /* C4 00 */
+ "\304\001Illegal Data Form for Reserve Track Command", /* C4 01 */
+ "\304\002Unable to Reserve Track, Because Track Mode has been Changed", /* C4 02 */
+
+ "\305\000Buffer error during recording", /* C5 00 */
+ "\307\000Disk Style mismatch", /* C7 00 */
+ "\312\000Power Calibration error", /* CA 00 */
+ "\313\000Write error (Fatal Error/Time out)", /* CB 00 */
+ "\314\000Not enough space (Leadin/Leadout space)", /* CC 00 */
+ "\315\000No track present to finalize", /* CD 00 */
+ "\317\000Unable to recover damaged disk", /* CF 00 */
+
+ "\320\000PMA area full (1000 blocks)", /* D0 00 */
+ "\321\000PCA area full (100 counts)", /* D1 00 */
+ "\322\000Recovery failed", /* D2 00 */
+ "\323\000Recovery needed", /* D3 00 */
+ NULL
+};
+
+static int
+cw7501_attach(SCSI *usalp, cdr_t *dp)
+{
+ usal_setnonstderrs(usalp, sd_cw7501_error_str);
+ return (0);
+}
+
+static int
+cw7501_init(SCSI *usalp, cdr_t *dp)
+{
+ return (cw7501_speed_select(usalp, dp, NULL));
+}
+
+static int
+cw7501_getdisktype(SCSI *usalp, cdr_t *dp)
+{
+ Ulong maxb = 0;
+ Uchar buf[256];
+ int ret;
+ dstat_t *dsp = dp->cdr_dstat;
+
+ if (xdebug > 0) {
+ usalp->silent++;
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ ret = cw7501_read_trackinfo(usalp, buf, 32, 0, 0);
+ if (ret >= 0)
+ usal_prbytes("TI EXIST-R (0): ", buf, 32 -usal_getresid(usalp));
+
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ ret = cw7501_read_trackinfo(usalp, buf, 32, 0, 1);
+ if (ret >= 0)
+ usal_prbytes("TI NWA (1): ", buf, 32 -usal_getresid(usalp));
+
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ ret = cw7501_read_trackinfo(usalp, buf, 32, 0, 2);
+ if (ret >= 0)
+ usal_prbytes("TI PMA (2): ", buf, 32 -usal_getresid(usalp));
+
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ ret = cw7501_read_trackinfo(usalp, buf, 32, 0, 3);
+ if (ret >= 0)
+ usal_prbytes("TI EXIST-ROM (3): ", buf, 32 -usal_getresid(usalp));
+ usalp->silent--;
+ }
+
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+
+ usalp->silent++;
+ ret = cw7501_read_trackinfo(usalp, buf, 12, 0, TI_NWA);
+ if (ret < 0 &&
+ (dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == RF_WRITE) {
+
+ /*
+ * Try to clear the dummy bit to reset the virtual
+ * drive status. Not all drives support it even though
+ * it is mentioned in the MMC standard.
+ */
+ if (lverbose)
+ printf("Trying to clear drive status.\n");
+ cw7501_rezero(usalp, 0, 1);
+ wait_unit_ready(usalp, 60);
+ ret = cw7501_read_trackinfo(usalp, buf, 12, 0, TI_NWA);
+ }
+ usalp->silent--;
+
+ if (ret >= 0) {
+ maxb = a_to_u_4_byte(&buf[8]);
+ if (maxb != 0)
+ maxb -= 150;
+ }
+ dsp->ds_maxblocks = maxb;
+
+ return (drive_getdisktype(usalp, dp));
+}
+
+
+static int
+cw7501_speed_select(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int len = 20;
+ int page = 0x20;
+ struct cw7501_mode_page_20 *xp20;
+ struct cw7501_mode_data md;
+ int count;
+ int speed = 1;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp) {
+ speed = *speedp;
+ } else {
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, page, "Speed information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ return (-1);
+ }
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp20 = (struct cw7501_mode_page_20 *)mp;
+ speed = xp20->speed;
+ }
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct cw7501_mode_page_20);
+
+ md.pagex.page20.p_code = 0x20;
+ md.pagex.page20.p_len = 0x02;
+ md.pagex.page20.speed = speed;
+
+ if (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2) < 0)
+ return (-1);
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct cw7501_mode_page_23);
+
+ md.pagex.page23.p_code = 0x23;
+ md.pagex.page23.p_len = 0x02;
+ md.pagex.page23.dummy = dummy?1:0;
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+cw7501_next_wr_addr(SCSI *usalp, track_t *trackp, long *ap)
+{
+ struct cw7501_nwa *nwa;
+ Uchar buf[256];
+ long next_addr;
+ int result = -1;
+
+
+ /*
+ * Reading info for current track may require doing the read_track_info
+ * with either the track number (if the track is currently being written)
+ * or with 0 (if the track hasn't been started yet and is invisible
+ */
+ nwa = (struct cw7501_nwa *)buf;
+
+ if (trackp != 0 && trackp->track > 0 && is_packet(trackp)) {
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+
+ usalp->silent++;
+ result = cw7501_read_trackinfo(usalp, buf, sizeof (*nwa),
+ trackp->trackno,
+ TI_NWA);
+ usalp->silent--;
+ }
+
+ if (result < 0) {
+ if (cw7501_read_trackinfo(usalp, buf, sizeof (*nwa),
+ 0, TI_NWA) < 0)
+ return (-1);
+ }
+ if (usalp->verbose)
+ usal_prbytes("track info:", buf,
+ 12-usal_getresid(usalp));
+ next_addr = a_to_4_byte(&nwa->nwa_nwa);
+ /*
+ * XXX Für TAO definitiv notwendig.
+ * XXX ABhängig von Auto-Pregap?
+ */
+ /* XXX */ next_addr += 150;
+ if (ap)
+ *ap = next_addr;
+ return (0);
+}
+
+static int
+cw7501_write(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ if (lverbose > 1 && islast)
+ printf("\nWriting last record for this track.\n");
+
+ return (write_xg0(usalp, bp, 0, size, blocks));
+}
+
+static int
+cw7501_write_leadin(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uint i;
+ long startsec = 0L;
+
+ if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) {
+ if (debug || lverbose) {
+ printf("Sending CUE sheet...\n");
+ flush();
+ }
+ if ((*dp->cdr_send_cue)(usalp, dp, trackp) < 0) {
+ errmsgno(EX_BAD, "Cannot send CUE sheet.\n");
+ return (-1);
+ }
+
+ /*
+ * Next writable address function does not work in DAO
+ * mode for this writer, so we just assume -150.
+ */
+ startsec = -150;
+ if (debug)
+ printf("SAO startsec: %ld\n", startsec);
+
+ if (trackp[0].flags & TI_TEXT) {
+ errmsgno(EX_BAD, "CD-Text unsupported in CW-7501 - ignoring.\n");
+ } else for (i = 1; i <= trackp->tracks; i++) {
+ trackp[i].trackstart += startsec +150;
+ }
+ }
+ return (0);
+}
+
+static Uchar db2phys[] = {
+ 0x00, /* 0 2352 bytes of raw data */
+ 0xFF, /* 1 2368 bytes (raw data + P/Q Subchannel) */
+ 0xFF, /* 2 2448 bytes (raw data + P-W Subchannel) */
+ 0xFF, /* 3 2448 bytes (raw data + P-W raw Subchannel)*/
+ 0xFF, /* 4 - Reserved */
+ 0xFF, /* 5 - Reserved */
+ 0xFF, /* 6 - Reserved */
+ 0xFF, /* 7 - Vendor specific */
+ 0x02, /* 8 2048 bytes Mode 1 (ISO/IEC 10149) */
+ 0x03, /* 9 2336 bytes Mode 2 (ISO/IEC 10149) */
+ 0xFF, /* 10 2048 bytes Mode 2 (CD-ROM XA form 1) */
+ 0x04, /* 11 2056 bytes Mode 2 (CD-ROM XA form 1) */
+ 0xFF, /* 12 2324 bytes Mode 2 (CD-ROM XA form 2) */
+ 0x08, /* 13 2332 bytes Mode 2 (CD-ROM XA 1/2+subhdr) */
+ 0xFF, /* 14 - Reserved */
+ 0xFF, /* 15 - Vendor specific */
+};
+
+static int
+cw7501_open_track(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct scsi_mode_page_header *mp;
+ Uchar mode[256];
+ int len = 0;
+ int page = 0x23;
+ struct cw7501_mode_page_23 *xp23;
+
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ if (trackp->pregapsize > 0 && (trackp->flags & TI_PREGAP) == 0) {
+ if (lverbose) {
+ printf("Writing pregap for track %d at %ld\n",
+ (int)trackp->trackno,
+ trackp->trackstart-trackp->pregapsize);
+ }
+ /*
+ * XXX Do we need to check isecsize too?
+ */
+ pad_track(usalp, dp, trackp,
+ trackp->trackstart-trackp->pregapsize,
+ (Llong)trackp->pregapsize*trackp->secsize,
+ FALSE, 0);
+ }
+ return (0);
+ }
+
+ if (select_secsize(usalp, trackp->secsize) < 0)
+ return (-1);
+
+ if (!get_mode_params(usalp, page, "Dummy/autopg information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ return (-1);
+ }
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp23 = (struct cw7501_mode_page_23 *)mp;
+ xp23->autopg = 1;
+ if (!set_mode_params(usalp, "Dummy/autopg page", mode, len, 0, trackp->secsize))
+ return (-1);
+
+ /*
+ * Set write modes for next track.
+ */
+ if (cw7501_set_mode(usalp, db2phys[trackp->dbtype & 0x0F],
+ st2mode[trackp->sectype&ST_MASK] | (is_copy(trackp) ? TM_ALLOW_COPY : 0),
+ 0, is_scms(trackp) ? 1 : 0,
+ trackp->trackno, 1, 0,
+ /* write mode TAO */ 0x01) < 0)
+ return (-1);
+
+ return (0);
+}
+
+
+static int
+cw7501_close_track(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ return (0);
+ }
+ return (scsi_flush_cache(usalp, FALSE));
+}
+
+static int
+cw7501_open_session(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct cw7501_mode_data md;
+ int count;
+
+ if (select_secsize(usalp, 2048) < 0)
+ return (-1);
+
+ /*
+ * Disable Auto Pregap when writing in SAO mode.
+ */
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ struct scsi_mode_page_header *mp;
+ Uchar mode[256];
+ int len = 0;
+ int page = 0x23;
+ struct cw7501_mode_page_23 *xp23;
+
+ if (!get_mode_params(usalp, page, "Dummy/autopg information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ return (-1);
+ }
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp23 = (struct cw7501_mode_page_23 *)mp;
+ xp23->autopg = 0;
+ if (!set_mode_params(usalp, "Dummy/autopg page", mode, len, 0, trackp->secsize))
+ return (-1);
+
+ return (0);
+ }
+
+ /*
+ * Set Disk Type and Disk ID.
+ */
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct cw7501_mode_page_24);
+
+ md.pagex.page24.p_code = 0x24;
+ md.pagex.page24.p_len = 0x0A;
+ md.pagex.page24.disktype = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+ i_to_4_byte(&md.pagex.page24.disk_id, 0x12345);
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+cw7501_fixate(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ return (scsi_flush_cache(usalp, FALSE));
+ }
+ /*
+ * 0x00 Finalize Disk (not appendable)
+ * 0x01 Finalize Session (allow next session)
+ * 0x10 Finalize track (variable packet writing) - Must fluch cache before
+ */
+ return (cw7501_finalize(usalp, 0, (track_base(trackp)->tracktype & TOCF_MULTI) ? 0x01 : 0x00));
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int
+cw7501_gen_cue(track_t *trackp, void *vcuep, BOOL needgap)
+{
+ int tracks = trackp->tracks;
+ int i;
+ struct cw7501_cue **cuep = vcuep;
+ struct cw7501_cue *cue;
+ struct cw7501_cue *cp;
+ int ncue = 0;
+ int icue = 0;
+ int pgsize;
+ msf_t m;
+ int ctl;
+ int df;
+ int scms;
+
+ cue = malloc(1);
+
+ for (i = 0; i <= tracks; i++) {
+ ctl = (st2mode[trackp[i].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[i]))
+ ctl |= TM_ALLOW_COPY << 4;
+ df = db2phys[trackp[i].dbtype & 0x0F];
+
+ if (trackp[i].isrc) { /* MCN or ISRC */
+ ncue += 2;
+ cue = realloc(cue, ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ if (i == 0) {
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[0], &cp->cs_tno, 7);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[7], &cp->cs_tno, 7);
+ } else {
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[0], &cp->cs_index, 6);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[6], &cp->cs_index, 6);
+ }
+ }
+ if (i == 0) { /* Lead in */
+ lba_to_msf(-150, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, 0, &m);
+ } else {
+ scms = 0;
+
+ if (is_scms(&trackp[i]))
+ scms = 0x80;
+ pgsize = trackp[i].pregapsize;
+ if (pgsize == 0 && needgap)
+ pgsize++;
+ lba_to_msf(trackp[i].trackstart-pgsize, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, scms, &m);
+
+ if (trackp[i].nindex == 1) {
+ lba_to_msf(trackp[i].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 1, df, scms, &m);
+ } else {
+ int idx;
+ long *idxlist;
+
+ ncue += trackp[i].nindex;
+ idxlist = trackp[i].tindex;
+ cue = realloc(cue, ncue * sizeof (*cue));
+
+ for (idx = 1; idx <= trackp[i].nindex; idx++) {
+ lba_to_msf(trackp[i].trackstart + idxlist[idx], &m);
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, idx, df, scms, &m);
+ }
+ }
+ }
+ }
+ /* Lead out */
+ ctl = (st2mode[trackp[tracks+1].sectype & ST_MASK]) << 4;
+ df = db2phys[trackp[tracks+1].dbtype & 0x0F];
+ lba_to_msf(trackp[tracks+1].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, 0xAA, 1, df, 0, &m);
+
+ if (lverbose > 1) {
+ for (i = 0; i < ncue; i++) {
+ usal_prbytes("", (Uchar *)&cue[i], 8);
+ }
+ }
+ if (cuep)
+ *cuep = cue;
+ else
+ free(cue);
+ return (ncue);
+}
+
+static void
+fillcue(struct cw7501_cue *cp /* The target cue entry */,
+ int ca /* Control/adr for this entry */,
+ int tno /* Track number for this entry */,
+ int idx /* Index for this entry */,
+ int dataform /* Data format for this entry */,
+ int scms /* Serial copy management */,
+ msf_t *mp /* MSF value for this entry */)
+{
+ cp->cs_ctladr = ca;
+ if (tno <= 99)
+ cp->cs_tno = to_bcd(tno);
+ else
+ cp->cs_tno = tno;
+ cp->cs_index = to_bcd(idx);
+ if (scms != 0)
+ dataform |= 0x10;
+ cp->cs_dataform = dataform;
+ cp->cs_extension = 0;
+ cp->cs_min = to_bcd(mp->msf_min);
+ cp->cs_sec = to_bcd(mp->msf_sec);
+ cp->cs_frame = to_bcd(mp->msf_frame);
+}
+
+static int
+cw7501_send_cue(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct cw7501_cue *cp;
+ int ncue;
+ int ret;
+ Uint i;
+ struct timeval starttime;
+ struct timeval stoptime;
+ int disktype;
+
+ disktype = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+
+ for (i = 1; i <= trackp->tracks; i++) {
+ if (trackp[i].tracksize < (tsize_t)0) {
+ errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
+ return (-1);
+ }
+ }
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, FALSE);
+
+ starttime.tv_sec = 0;
+ starttime.tv_usec = 0;
+ stoptime = starttime;
+ gettimeofday(&starttime, (struct timezone *)0);
+
+ usalp->silent++;
+ ret = cw7501_write_dao(usalp, (Uchar *)cp, ncue*8, disktype);
+ usalp->silent--;
+ free(cp);
+ if (ret < 0) {
+ errmsgno(EX_BAD, "CUE sheet not accepted. Retrying with minimum pregapsize = 1.\n");
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, TRUE);
+ ret = cw7501_write_dao(usalp, (Uchar *)cp, ncue*8, disktype);
+ free(cp);
+ }
+ if (ret >= 0 && lverbose) {
+ gettimeofday(&stoptime, (struct timezone *)0);
+ prtimediff("Write Lead-in time: ", &starttime, &stoptime);
+ }
+ return (ret);
+}
+
+/*--------------------------------------------------------------------------*/
+static int
+cw7501_rezero(SCSI *usalp, int reset, int dwreset)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_REZERO_UNIT;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[5] |= reset ? 0x80 : 0;
+ scmd->cdb.cmd_cdb[5] |= dwreset ? 0x40 : 0;
+
+ usalp->cmdname = "cw7501 rezero";
+
+ return (usal_cmd(usalp));
+}
+
+
+static int
+cw7501_read_trackinfo(SCSI *usalp, Uchar *bp, int count, int track, int mode)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t) scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)bp;
+ scmd->size = count;
+ scmd->flags = SCG_RECV_DATA | SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE9;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[6] = track;
+ g1_cdblen(&scmd->cdb.g1_cdb, count);
+ scmd->cdb.cmd_cdb[9] = (mode & 3) << 6;
+
+ usalp->cmdname = "cw7501 read_track_information";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+cw7501_write_dao(SCSI *usalp, Uchar *bp, int len, int disktype)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)bp;
+ scmd->size = len;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE6;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[2] = disktype;
+ g1_cdblen(&scmd->cdb.g1_cdb, len);
+
+ usalp->cmdname = "cw7501 write_dao";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * XXX CW-7501 also needs "control", so we need to make a different
+ * XXX driver interface.
+ */
+static int
+cw7501_reserve_track(SCSI *usalp, unsigned long len)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE7;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+/* scmd->cdb.cmd_cdb[2] = control & 0x0F;*/
+ i_to_4_byte(&scmd->cdb.cmd_cdb[5], len);
+
+ usalp->cmdname = "cw7501 reserve_track";
+
+ comerrno(EX_BAD, "Control (as in set mode) missing.\n");
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+cw7501_set_mode(SCSI *usalp, int phys_form, int control, int subc,
+ int alt, int trackno, int tindex, int packet_size,
+ int write_mode)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE2;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[2] = phys_form & 0x0F;
+ scmd->cdb.cmd_cdb[3] = (control & 0x0F) << 4;
+ scmd->cdb.cmd_cdb[3] |= subc ? 2 : 0;
+ scmd->cdb.cmd_cdb[3] |= alt ? 1 : 0;
+ scmd->cdb.cmd_cdb[4] = trackno;
+ scmd->cdb.cmd_cdb[5] = tindex;
+ i_to_3_byte(&scmd->cdb.cmd_cdb[6], packet_size);
+ scmd->cdb.cmd_cdb[9] = (write_mode & 0x03) << 6;
+
+ usalp->cmdname = "cw7501 set_mode";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+cw7501_finalize(SCSI *usalp, int pad, int fixed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+ scmd->cdb.g1_cdb.cmd = 0xE3;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[1] = pad ? 1 : 0;
+ scmd->cdb.cmd_cdb[8] = fixed & 0x03;
+
+ usalp->cmdname = "cw7501 finalize";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
diff --git a/wodim/drv_jvc.c b/wodim/drv_jvc.c
new file mode 100644
index 0000000..ee2bcc2
--- /dev/null
+++ b/wodim/drv_jvc.c
@@ -0,0 +1,1433 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/** @(#)drv_jvc.c 1.82 05/05/16 Copyright 1997-2005 J. Schilling */
+/*
+ * CDR device implementation for
+ * JVC/TEAC
+ *
+ * Copyright (c) 1997-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+/*#define XXDEBUG*/
+/*#define XXBUFFER*/
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <unixstd.h>
+#ifdef XXDEBUG
+#include <stdxlib.h>
+#endif
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+
+/* just a hack */
+long lba_addr;
+BOOL last_done;
+
+/*
+ * macros for building MSF values from LBA
+ */
+#define LBA_MIN(x) ((x)/(60*75))
+#define LBA_SEC(x) (((x)%(60*75))/75)
+#define LBA_FRM(x) ((x)%75)
+#define MSF_CONV(a) ((((a)%(unsigned)100)/10)*16 + ((a)%(unsigned)10))
+
+extern int lverbose;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+struct teac_mode_page_21 { /* teac dummy selection */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x01 = 1 Byte */
+ Ucbit dummy : 2;
+ Ucbit res : 6;
+};
+#else
+struct teac_mode_page_21 { /* teac dummy selection */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x01 = 1 Byte */
+ Ucbit res : 6;
+ Ucbit dummy : 2;
+};
+#endif
+
+struct teac_mode_page_31 { /* teac speed selection */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Byte */
+ Uchar speed;
+ Uchar res;
+};
+
+struct cdd_52x_mode_data {
+ struct scsi_mode_header header;
+ union cdd_pagex {
+ struct teac_mode_page_21 teac_page21;
+ struct teac_mode_page_31 teac_page31;
+ } pagex;
+};
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct pgm_subcode { /* subcode for progam area */
+ Uchar subcode;
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar track;
+ Uchar index;
+};
+
+#else
+
+struct pgm_subcode { /* subcode for progam area */
+ Uchar subcode;
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar track;
+ Uchar index;
+};
+
+#endif
+
+#define set_pgm_subcode(sp, t, c, a, tr, idx) (\
+ (sp)->subcode = (t), \
+ (sp)->control = (c), \
+ (sp)->addr = (a), \
+ (sp)->track = MSF_CONV(tr), \
+ (sp)->index = (idx))
+
+#define SC_P 1 /* Subcode defines pre-gap (Pause) */
+#define SC_TR 0 /* Subcode defines track data */
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+typedef struct lin_subcode { /* subcode for lead in area */
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar track;
+ Uchar msf[3];
+} lsc_t;
+
+#else
+
+typedef struct lin_subcode { /* subcode for lead in area */
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar track;
+ Uchar msf[3];
+} lsc_t;
+
+#endif
+
+#define set_toc_subcode(sp, c, a, tr, bno) (\
+ ((lsc_t *)sp)->control = (c), \
+ ((lsc_t *)sp)->addr = (a), \
+ ((lsc_t *)sp)->track = MSF_CONV(tr), \
+ ((lsc_t *)sp)->msf[0] = MSF_CONV(LBA_MIN(bno)), \
+ ((lsc_t *)sp)->msf[1] = MSF_CONV(LBA_SEC(bno)), \
+ ((lsc_t *)sp)->msf[2] = MSF_CONV(LBA_FRM(bno)), \
+ &((lsc_t *)sp)->msf[3])
+
+#define set_lin_subcode(sp, c, a, pt, min, sec, frm) (\
+ ((lsc_t *)sp)->control = (c), \
+ ((lsc_t *)sp)->addr = (a), \
+ ((lsc_t *)sp)->track = (pt), \
+ ((lsc_t *)sp)->msf[0] = (min), \
+ ((lsc_t *)sp)->msf[1] = (sec), \
+ ((lsc_t *)sp)->msf[2] = (frm), \
+ &((lsc_t *)sp)->msf[3])
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct upc_subcode { /* subcode for upc/bar code */
+ Uchar res;
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar upc[13];
+};
+
+#else
+
+struct upc_subcode { /* subcode for upc/bar code */
+ Uchar res;
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar upc[13];
+};
+
+#endif
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct isrc_subcode { /* subcode for ISRC code */
+ Uchar res;
+ Ucbit addr : 4;
+ Ucbit control : 4;
+ Uchar isrc[12];
+ Uchar res14;
+};
+
+#else
+
+struct isrc_subcode { /* subcode for ISRC code */
+ Uchar res;
+ Ucbit control : 4;
+ Ucbit addr : 4;
+ Uchar isrc[12];
+ Uchar res14;
+};
+
+#endif
+
+
+static int teac_attach(SCSI *usalp, cdr_t *dp);
+static int teac_init(SCSI *usalp, cdr_t *dp);
+static int teac_getdisktype(SCSI *usalp, cdr_t *dp);
+static int speed_select_teac(SCSI *usalp, cdr_t *dp, int *speedp);
+static int select_secsize_teac(SCSI *usalp, track_t *trackp);
+static int next_wr_addr_jvc(SCSI *usalp, track_t *, long *ap);
+static int write_teac_xg1(SCSI *usalp, caddr_t, long, long, int, BOOL);
+static int cdr_write_teac(SCSI *usalp, caddr_t bp, long sectaddr, long size,
+ int blocks, BOOL islast);
+static int open_track_jvc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int teac_fixation(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int close_track_teac(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int teac_open_session(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int initsub_teac(SCSI *usalp, int toctype, int multi);
+static int teac_doopc(SCSI *usalp);
+static int teac_opc(SCSI *usalp, caddr_t, int cnt, int doopc);
+static int opt_power_judge(SCSI *usalp, int judge);
+static int clear_subcode(SCSI *usalp);
+static int set_limits(SCSI *usalp, long lba, long length);
+static int set_subcode(SCSI *usalp, Uchar *subcode_data, int length);
+static int read_disk_info_teac(SCSI *usalp, Uchar *data, int length,
+ int type);
+static int teac_freeze(SCSI *usalp, int bp_flag);
+static int teac_wr_pma(SCSI *usalp);
+static int teac_rd_pma(SCSI *usalp);
+static int next_wr_addr_teac(SCSI *usalp, long start_lba, long last_lba);
+static int blank_jvc(SCSI *usalp, cdr_t *dp, long addr, int blanktype);
+static int buf_cap_teac(SCSI *usalp, long *sp, long *fp);
+static long read_peak_buffer_cap_teac(SCSI *usalp);
+static int buffer_inquiry_teac(SCSI *usalp, int fmt);
+#ifdef XXBUFFER
+static void check_buffer_teac(SCSI *usalp);
+#endif
+#ifdef XXDEBUG
+static void xxtest_teac(SCSI *usalp);
+#endif
+
+
+cdr_t cdr_teac_cdr50 = {
+ 0, 0,
+/* CDR_TAO|CDR_SAO|CDR_SWABAUDIO|CDR_NO_LOLIMIT,*/
+ CDR_TAO|CDR_SWABAUDIO|CDR_NO_LOLIMIT,
+ CDR_CDRW_ALL,
+ 2, 4,
+ "teac_cdr50",
+ "driver for Teac CD-R50S, Teac CD-R55S, JVC XR-W2010, Pinnacle RCD-5020",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ teac_attach,
+ teac_init,
+ teac_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_cap_teac,
+ cmd_dummy, /* recovery_needed */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_teac,
+ select_secsize,
+ next_wr_addr_jvc,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ cdr_write_teac,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_jvc,
+ close_track_teac,
+ teac_open_session,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ teac_fixation,
+ cmd_dummy, /* stats */
+/* blank_dummy,*/
+ blank_jvc,
+ format_dummy,
+ teac_opc,
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+static int
+teac_init(SCSI *usalp, cdr_t *dp)
+{
+ return (speed_select_teac(usalp, dp, NULL));
+}
+
+static int
+teac_getdisktype(SCSI *usalp, cdr_t *dp)
+{
+ dstat_t *dsp = dp->cdr_dstat;
+ struct scsi_mode_data md;
+ int count = sizeof (struct scsi_mode_header) +
+ sizeof (struct scsi_mode_blockdesc);
+ int len;
+ int page = 0;
+ long l;
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ (void) test_unit_ready(usalp);
+ if (mode_sense(usalp, (Uchar *)&md, count, page, 0) < 0) { /* Page n current */
+ return (-1);
+ } else {
+ len = ((struct scsi_mode_header *)&md)->sense_data_len + 1;
+ }
+ if (((struct scsi_mode_header *)&md)->blockdesc_len < 8)
+ return (-1);
+
+ l = a_to_u_3_byte(md.blockdesc.nlblock);
+ dsp->ds_maxblocks = l;
+ return (drive_getdisktype(usalp, dp));
+}
+
+static int
+speed_select_teac(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ struct cdd_52x_mode_data md;
+ int count;
+ int status;
+ int speed = 1;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp)
+ speed = *speedp;
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct teac_mode_page_21);
+
+ md.pagex.teac_page21.p_code = 0x21;
+ md.pagex.teac_page21.p_len = 0x01;
+ md.pagex.teac_page21.dummy = dummy?3:0;
+
+ status = mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2);
+ if (status < 0)
+ return (status);
+
+ if (speedp == 0)
+ return (0);
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct teac_mode_page_31);
+
+ speed >>= 1;
+ md.pagex.teac_page31.p_code = 0x31;
+ md.pagex.teac_page31.p_len = 0x02;
+ md.pagex.teac_page31.speed = speed;
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+select_secsize_teac(SCSI *usalp, track_t *trackp)
+{
+ struct scsi_mode_data md;
+ int count = sizeof (struct scsi_mode_header) +
+ sizeof (struct scsi_mode_blockdesc);
+ int len;
+ int page = 0;
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ (void) test_unit_ready(usalp);
+ if (mode_sense(usalp, (Uchar *)&md, count, page, 0) < 0) { /* Page n current */
+ return (-1);
+ } else {
+ len = ((struct scsi_mode_header *)&md)->sense_data_len + 1;
+ }
+ if (((struct scsi_mode_header *)&md)->blockdesc_len < 8)
+ return (-1);
+
+ md.header.sense_data_len = 0;
+ md.header.blockdesc_len = 8;
+
+ md.blockdesc.density = 1;
+ if (trackp->secsize == 2352)
+ md.blockdesc.density = 4;
+ i_to_3_byte(md.blockdesc.lblen, trackp->secsize);
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+next_wr_addr_jvc(SCSI *usalp, track_t *trackp, long *ap)
+{
+ if (trackp != 0 && trackp->track > 0) {
+ *ap = lba_addr;
+ } else {
+ long nwa;
+
+ if (read_B0(usalp, TRUE, &nwa, NULL) < 0)
+ return (-1);
+
+ *ap = nwa + 150;
+ }
+ return (0);
+}
+
+static int
+write_teac_xg1(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL extwr /* is an extended write */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+/* scmd->flags = SCG_DISRE_ENA;*/
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = SC_EWRITE;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, sectaddr);
+ g1_cdblen(&scmd->cdb.g1_cdb, blocks);
+ scmd->cdb.g1_cdb.vu_97 = extwr;
+
+ usalp->cmdname = "write_teac_g1";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (size - usal_getresid(usalp));
+}
+
+static int
+cdr_write_teac(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ int ret;
+
+ if (islast)
+ last_done = TRUE;
+
+ ret = write_teac_xg1(usalp, bp, sectaddr, size, blocks, !islast);
+ if (ret < 0)
+ return (ret);
+
+ lba_addr = sectaddr + blocks;
+#ifdef XXBUFFER
+ check_buffer_teac(usalp);
+#endif
+ return (ret);
+}
+
+static int
+open_track_jvc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int status;
+ long blocks;
+ long pregapsize;
+ struct pgm_subcode sc;
+
+ last_done = FALSE;
+
+ if (select_secsize_teac(usalp, trackp) < 0)
+ return (-1);
+
+ status = clear_subcode(usalp);
+/*next_wr_addr_teac(usalp);*/
+ if (status < 0)
+ return (status);
+
+if (trackp->pregapsize != 0) {
+ if (lverbose > 1) {
+ printf("set_limits(%ld, %ld)-> %ld\n",
+ lba_addr, trackp->pregapsize, lba_addr + trackp->pregapsize);
+ }
+
+ status = set_limits(usalp, lba_addr, trackp->pregapsize);
+ if (status < 0)
+ return (status);
+
+ /*
+ * Set pre-gap (pause - index 0)
+ */
+ set_pgm_subcode(&sc, SC_P,
+ st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 0);
+
+ if (lverbose > 1)
+ usal_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));
+
+ status = set_subcode(usalp, (Uchar *)&sc, sizeof (sc));
+ if (status < 0)
+ return (status);
+
+ pregapsize = trackp->pregapsize;
+ if (!is_audio(trackp)) {
+ lba_addr += 5; /* link & run in blocks */
+ pregapsize -= 5;
+ }
+ if (lverbose > 1) {
+ printf("pad_track(%ld, %ld)-> %ld\n",
+ lba_addr, pregapsize, lba_addr + pregapsize);
+ }
+ /*
+ * XXX Do we need to check isecsize too?
+ */
+ if (pad_track(usalp, dp, trackp,
+ lba_addr, (Llong)pregapsize*trackp->secsize,
+ FALSE, (Llong *)0) < 0)
+ return (-1);
+}
+
+ blocks = trackp->tracksize/trackp->secsize +
+ (trackp->tracksize%trackp->secsize?1:0);
+ blocks += trackp->padsecs;
+ if (blocks < 300)
+ blocks = 300;
+ if (!is_audio(trackp))
+ blocks += 2;
+if (!is_last(trackp) && trackp[1].pregapsize == 0)
+ blocks -= 150;
+
+ /*
+ * set the limits for the new subcode - seems to apply to all
+ * of the data track.
+ * Unknown tracksize is handled in open_session.
+ * We definitely need to know the tracksize in this driver.
+ */
+ if (lverbose > 1) {
+ printf("set_limits(%ld, %ld)-> %ld\n",
+ lba_addr, blocks, lba_addr + blocks);
+ }
+ status = set_limits(usalp, lba_addr, blocks);
+ if (status < 0)
+ return (status);
+
+ /*
+ * Set track start (index 1)
+ */
+ set_pgm_subcode(&sc, SC_TR,
+ st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 1);
+
+ if (lverbose > 1)
+ usal_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));
+
+ status = set_subcode(usalp, (Uchar *)&sc, sizeof (sc));
+ if (status < 0)
+ return (status);
+
+if (!is_last(trackp) && trackp[1].pregapsize == 0) {
+ blocks += lba_addr;
+ pregapsize = 150;
+
+ if (lverbose > 1) {
+ printf("set_limits(%ld, %ld)-> %ld\n",
+ blocks, pregapsize, blocks + pregapsize);
+ }
+
+ status = set_limits(usalp, blocks, pregapsize);
+ if (status < 0)
+ return (status);
+
+ /*
+ * Set pre-gap (pause - index 0)
+ */
+ trackp++;
+ set_pgm_subcode(&sc, SC_P,
+ st2mode[trackp->sectype&ST_MASK], ADR_POS, trackp->trackno, 0);
+
+ if (lverbose > 1)
+ usal_prbytes("Subcode:", (Uchar *)&sc, sizeof (sc));
+
+ status = set_subcode(usalp, (Uchar *)&sc, sizeof (sc));
+ if (status < 0)
+ return (status);
+}
+ return (status);
+}
+
+static char sector[3000];
+
+static int
+close_track_teac(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int ret = 0;
+
+ if (!last_done) {
+ printf("WARNING: adding dummy block to close track.\n");
+ /*
+ * need read sector size
+ * XXX do we really need this ?
+ * XXX if we need this can we set blocks to 0 ?
+ */
+ ret = write_teac_xg1(usalp, sector, lba_addr, 2352, 1, FALSE);
+ lba_addr++;
+ }
+ if (!is_audio(trackp))
+ lba_addr += 2;
+ teac_wr_pma(usalp);
+ return (ret);
+}
+
+
+
+static const char *sd_teac50_error_str[] = {
+ "\100\200diagnostic failure on component parts", /* 40 80 */
+ "\100\201diagnostic failure on memories", /* 40 81 */
+ "\100\202diagnostic failure on cd-rom ecc circuit", /* 40 82 */
+ "\100\203diagnostic failure on gate array", /* 40 83 */
+ "\100\204diagnostic failure on internal SCSI controller", /* 40 84 */
+ "\100\205diagnostic failure on servo processor", /* 40 85 */
+ "\100\206diagnostic failure on program rom", /* 40 86 */
+ "\100\220thermal sensor failure", /* 40 90 */
+ "\200\000controller prom error", /* 80 00 */ /* JVC */
+ "\201\000no disk present - couldn't get focus", /* 81 00 */ /* JVC */
+ "\202\000no cartridge present", /* 82 00 */ /* JVC */
+ "\203\000unable to spin up", /* 83 00 */ /* JVC */
+ "\204\000addr exceeded the last valid block addr", /* 84 00 */ /* JVC */
+ "\205\000sync error", /* 85 00 */ /* JVC */
+ "\206\000address can't find or not data track", /* 86 00 */ /* JVC */
+ "\207\000missing track", /* 87 00 */ /* JVC */
+ "\213\000cartridge could not be ejected", /* 8B 00 */ /* JVC */
+ "\215\000audio not playing", /* 8D 00 */ /* JVC */
+ "\216\000read toc error", /* 8E 00 */ /* JVC */
+ "\217\000a blank disk is detected by read toc", /* 8F 00 */
+ "\220\000pma less disk - not a recordable disk", /* 90 00 */
+ "\223\000mount error", /* 93 00 */ /* JVC */
+ "\224\000toc less disk", /* 94 00 */
+ "\225\000disc information less disk", /* 95 00 */ /* JVC */
+ "\226\000disc information read error", /* 96 00 */ /* JVC */
+ "\227\000linear velocity measurement error", /* 97 00 */ /* JVC */
+ "\230\000drive sequence stop", /* 98 00 */ /* JVC */
+ "\231\000actuator velocity control error", /* 99 00 */ /* JVC */
+ "\232\000slider velocity control error", /* 9A 00 */ /* JVC */
+ "\233\000opc initialize error", /* 9B 00 */
+ "\233\001power calibration not executed", /* 9B 01 */
+ "\234\000opc execution eror", /* 9C 00 */
+ "\234\001alpc error - opc execution", /* 9C 01 */
+ "\234\002opc execution timeout", /* 9C 02 */
+ "\245\000disk application code does not match host application code", /* A5 00 */
+ "\255\000completed preview write", /* AD 00 */
+ "\256\000invalid B0 value", /* AE 00 */ /* JVC */
+ "\257\000pca area full", /* AF 00 */
+ "\260\000efm isn't detected", /* B0 00 */ /* JVC */
+ "\263\000no logical sector", /* B3 00 */ /* JVC */
+ "\264\000full pma area", /* B4 00 */
+ "\265\000read address is atip area - blank", /* B5 00 */
+ "\266\000write address is efm area - aleady written", /* B6 00 */
+ "\271\000abnormal spinning - servo irq", /* B9 00 */ /* JVC */
+ "\272\000no write data - buffer empty", /* BA 00 */
+ "\273\000write emergency occurred", /* BB 00 */
+ "\274\000read timeout", /* BC 00 */ /* JVC */
+ "\277\000abnormal spin - nmi", /* BF 00 */ /* JVC */
+ "\301\0004th run-in block detected", /* C1 00 */
+ "\302\0003rd run-in block detected", /* C2 00 */
+ "\303\0002nd run-in block detected", /* C3 00 */
+ "\304\0001st run-in block detected", /* C4 00 */
+ "\305\000link block detected", /* C5 00 */
+ "\306\0001st run-out block detected", /* C6 00 */
+ "\307\0002nd run-out block detected", /* C7 00 */
+ "\314\000write request means mixed data mode", /* CC 00 */
+ "\315\000unable to ensure reliable writing with the inserted disk - unsupported disk", /* CD 00 */
+ "\316\000unable to ensure reliable writing as the inserted disk does not support speed", /* CE 00 */
+ "\317\000unable to ensure reliable writing as the inserted disk has no char id code", /* CF 00 */
+ NULL
+};
+
+static int
+teac_attach(SCSI *usalp, cdr_t *dp)
+{
+ usal_setnonstderrs(usalp, sd_teac50_error_str);
+#ifdef XXDEBUG
+ xxtest_teac(usalp);
+ exit(0);
+#endif
+ return (0);
+}
+
+static int
+teac_fixation(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ long lba;
+ int status;
+ Uchar *sp;
+ Uint i;
+extern char *buf;
+
+ if (trackp->tracks < 1) {
+ /*
+ * We come here if wodim isonly called with the -fix option.
+ * As long as we cannot read and interpret the PMA, we must
+ * abort here.
+ */
+ teac_rd_pma(usalp);
+/* errmsgno(EX_BAD, "Cannot fixate zero track disk.\n");*/
+ errmsgno(EX_BAD, "Cannot fixate without track list (not yet implemented).\n");
+ return (-1);
+ }
+ sp = (Uchar *)buf;
+
+ sleep(1);
+
+ status = clear_subcode(usalp);
+ sleep(1);
+ if (status < 0)
+ return (status);
+
+ sp[0] = 0; /* reserved */
+ sp[1] = 0; /* reserved */
+ sp[2] = 0; /* Q TNO */
+
+ sp = &sp[3]; /* point past header */
+
+ /*
+ * Set up TOC entries for all tracks
+ */
+ for (i = 1; i <= trackp->tracks; i++) {
+ lba = trackp[i].trackstart+150; /* MSF=00:02:00 is LBA=0 */
+
+ sp = set_toc_subcode(sp,
+ /* ctrl/adr for this track */
+ st2mode[trackp[i].sectype&ST_MASK], ADR_POS,
+ trackp[i].trackno, lba);
+ }
+
+ /*
+ * Set first track on disk
+ *
+ * XXX We set the track type for the lead-in to the track type
+ * XXX of the first track. The TEAC manual states that we should use
+ * XXX audio if the disk contains both, audio and data tracks.
+ */
+ sp = set_lin_subcode(sp,
+ /* ctrl/adr for first track */
+ st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
+ 0xA0, /* Point A0 */
+ trackp[1].trackno, /* first track # */
+ toc2sess[track_base(trackp)->tracktype & TOC_MASK], /* disk type */
+ 0); /* reserved */
+
+ /*
+ * Set last track on disk
+ */
+ sp = set_lin_subcode(sp,
+ /* ctrl/adr for first track */
+ st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
+ 0xA1, /* Point A1 */
+ MSF_CONV(trackp[trackp->tracks].trackno), /* last track # */
+ 0, /* reserved */
+ 0); /* reserved */
+
+ /*
+ * Set start of lead out area in MSF
+ * MSF=00:02:00 is LBA=0
+ */
+ lba = lba_addr + 150;
+ if (lverbose > 1)
+ printf("lba: %ld lba_addr: %ld\n", lba, lba_addr);
+
+ if (lverbose > 1)
+ printf("Lead out start: (%02d:%02d/%02d)\n",
+ minutes(lba*2352),
+ seconds(lba*2352),
+ frames(lba*2352));
+
+ sp = set_lin_subcode(sp,
+ /* ctrl/adr for first track */
+ st2mode[trackp[1].sectype&ST_MASK], ADR_POS,
+ 0xA2, /* Point A2 */
+ MSF_CONV(LBA_MIN(lba)),
+ MSF_CONV(LBA_SEC(lba)),
+ MSF_CONV(LBA_FRM(lba)));
+
+ status = sp - ((Uchar *)buf);
+ if (lverbose > 1) {
+ printf("Subcode len: %d\n", status);
+ usal_prbytes("Subcode:", (Uchar *)buf, status);
+ }
+ status = set_subcode(usalp, (Uchar *)buf, status);
+ sleep(1);
+ if (status < 0)
+ return (status);
+
+ /*
+ * now write the toc
+ */
+ status = teac_freeze(usalp, (track_base(trackp)->tracktype & TOCF_MULTI) == 0);
+ return (status);
+
+}
+
+static int
+teac_open_session(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uint i;
+
+ for (i = 1; i <= trackp->tracks; i++) {
+ if (trackp[i].tracksize < (tsize_t)0) {
+ /*
+ * XXX How about setting the subcode range to infinity.
+ * XXX and correct it in clode track before writing
+ * XXX the PMA?
+ */
+ errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
+ return (-1);
+ }
+ }
+ return (initsub_teac(usalp, track_base(trackp)->tracktype & TOC_MASK,
+ track_base(trackp)->tracktype & TOCF_MULTI));
+}
+
+static int
+initsub_teac(SCSI *usalp, int toctype, int multi)
+{
+ int status;
+
+ usalp->silent++;
+ if (read_B0(usalp, TRUE, &lba_addr, NULL) < 0)
+ lba_addr = -150;
+ usalp->silent--;
+
+ status = clear_subcode(usalp);
+ if (status < 0)
+ return (status);
+
+ return (0);
+}
+
+static int
+teac_doopc(SCSI *usalp)
+{
+ int status;
+
+ if (lverbose) {
+ fprintf(stdout, "Judging disk...");
+ flush();
+ }
+ status = opt_power_judge(usalp, 1);
+ if (status < 0) {
+ printf("\n");
+ return (status);
+ }
+ if (lverbose) {
+ fprintf(stdout, "done.\nCalibrating laser...");
+ flush();
+ }
+
+ status = opt_power_judge(usalp, 0);
+ if (lverbose) {
+ fprintf(stdout, "done.\n");
+ }
+ /*
+ * Check for error codes 0xCD ... 0xCF
+ */
+ usalp->silent++;
+ if (next_wr_addr_teac(usalp, -1, -1) < 0) {
+ if (usalp->verbose == 0 && usal_sense_key(usalp) != SC_ILLEGAL_REQUEST)
+ usal_printerr(usalp);
+ }
+ usalp->silent--;
+ return (status);
+}
+
+static int
+teac_opc(SCSI *usalp, caddr_t bp, int cnt, int doopc)
+{
+ int status;
+ int count = 0;
+
+ do {
+ status = teac_doopc(usalp);
+ } while (++count <= 1 && status < 0);
+
+ return (status);
+}
+
+/*--------------------------------------------------------------------------*/
+#define SC_SET_LIMITS 0xb3 /* teac 12 byte command */
+#define SC_SET_SUBCODE 0xc2 /* teac 10 byte command */
+#define SC_READ_PMA 0xc4 /* teac 10 byte command */
+#define SC_READ_DISK_INFO 0xc7 /* teac 10 byte command */
+#define SC_BUFFER_INQUIRY 0xe0 /* teac 12 byte command */
+
+#define SC_WRITE_PMA 0xe1 /* teac 12 byte command */
+#define SC_FREEZE 0xe3 /* teac 12 byte command */
+#define SC_OPC_EXECUTE 0xec /* teac 12 byte command */
+#define SC_CLEAR_SUBCODE 0xe4 /* teac 12 byte command */
+#define SC_NEXT_WR_ADDRESS 0xe6 /* teac 12 byte command */
+
+#define SC_READ_PEAK_BUF_CAP 0xef /* teac 12 byte command */
+
+/*
+ * Optimum power calibration for Teac Drives.
+ */
+static int
+opt_power_judge(SCSI *usalp, int judge)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 60;
+
+ scmd->cdb.g5_cdb.cmd = SC_OPC_EXECUTE;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.reladr = judge; /* Judge the Disc */
+
+ usalp->cmdname = "opt_power_judge";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Clear subcodes for Teac Drives.
+ */
+static int
+clear_subcode(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_CLEAR_SUBCODE;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.addr[3] = 0x80;
+
+ usalp->cmdname = "clear subcode";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Set limits for command linking for Teac Drives.
+ */
+static int
+set_limits(SCSI *usalp, long lba, long length)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_SET_LIMITS;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ i_to_4_byte(&scmd->cdb.g5_cdb.addr[0], lba);
+ i_to_4_byte(&scmd->cdb.g5_cdb.count[0], length);
+
+ usalp->cmdname = "set limits";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Set subcode for Teac Drives.
+ */
+static int
+set_subcode(SCSI *usalp, Uchar *subcode_data, int length)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)subcode_data;
+ scmd->size = length;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g1_cdb.cmd = SC_SET_SUBCODE;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, length);
+
+ usalp->cmdname = "set subcode";
+
+ return (usal_cmd(usalp));
+}
+
+static int
+read_disk_info_teac(SCSI *usalp, Uchar *data, int length, int type)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)data;
+ scmd->size = length;
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g1_cdb.cmd = SC_READ_DISK_INFO;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ scmd->cdb.g1_cdb.reladr = type & 1;
+ scmd->cdb.g1_cdb.res = (type & 2) >> 1;
+
+ usalp->cmdname = "read disk info teac";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Perform the freeze command for Teac Drives.
+ */
+static int
+teac_freeze(SCSI *usalp, int bp_flag)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+
+ scmd->cdb.g5_cdb.cmd = SC_FREEZE;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.addr[3] = bp_flag ? 0x80 : 0;
+
+ usalp->cmdname = "teac_freeze";
+
+ return (usal_cmd(usalp));
+}
+
+static int
+teac_wr_pma(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_WRITE_PMA;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "teac_write_pma";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Read PMA for Teac Drives.
+ */
+static int
+teac_rd_pma(SCSI *usalp)
+{
+ unsigned char xx[256];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g1_cdb.cmd = SC_READ_PMA;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ g1_cdblen(&scmd->cdb.g1_cdb, sizeof (xx));
+
+ usalp->cmdname = "teac_read_pma";
+
+/* return (usal_cmd(usalp));*/
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usalp->verbose) {
+ usal_prbytes("PMA Data", xx, sizeof (xx) - usal_getresid(usalp));
+ }
+ if (lverbose) {
+ unsigned i;
+ Uchar *p;
+
+ usal_prbytes("PMA Header: ", xx, 4);
+ i = xx[2];
+ p = &xx[4];
+ for (; i <= xx[3]; i++) {
+ usal_prbytes("PMA: ", p, 10);
+ p += 10;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Next writable address for Teac Drives.
+ */
+static int
+next_wr_addr_teac(SCSI *usalp, long start_lba, long last_lba)
+{
+ unsigned char xx[256];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_NEXT_WR_ADDRESS;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ i_to_4_byte(&scmd->cdb.g5_cdb.addr[0], start_lba);
+ i_to_4_byte(&scmd->cdb.g5_cdb.count[0], last_lba);
+
+ if (usalp->verbose)
+ printf("start lba: %ld last lba: %ld\n",
+ start_lba, last_lba);
+
+ usalp->cmdname = "next writable address";
+
+/* return (usal_cmd(usalp));*/
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usalp->verbose) {
+ usal_prbytes("WRa Data", xx, sizeof (xx) - usal_getresid(usalp));
+ printf("NWA: %ld\n", a_to_4_byte(xx));
+ }
+ return (0);
+}
+
+static int
+blank_jvc(SCSI *usalp, cdr_t *dp, long addr, int blanktype)
+{
+ extern char *blank_types[];
+
+ if (lverbose) {
+ printf("Blanking %s\n", blank_types[blanktype & 0x07]);
+ flush();
+ }
+
+ return (scsi_blank(usalp, addr, blanktype, FALSE));
+}
+
+static int
+buf_cap_teac(SCSI *usalp, long *sp, long *fp)
+{
+ Ulong freespace;
+ Ulong bufsize;
+ long ret;
+ int per;
+
+ ret = read_peak_buffer_cap_teac(usalp);
+ if (ret < 0)
+ return (-1);
+ bufsize = ret;
+ freespace = 0;
+ if (sp)
+ *sp = bufsize;
+ if (fp)
+ *fp = freespace;
+
+ if (usalp->verbose || (sp == 0 && fp == 0))
+ printf("BFree: %ld K BSize: %ld K\n", freespace >> 10, bufsize >> 10);
+
+ if (bufsize == 0)
+ return (0);
+ per = (100 * (bufsize - freespace)) / bufsize;
+ if (per < 0)
+ return (0);
+ if (per > 100)
+ return (100);
+ return (per);
+}
+
+static long
+read_peak_buffer_cap_teac(SCSI *usalp)
+{
+ Uchar xx[4];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_READ_PEAK_BUF_CAP;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "read peak buffer capacity";
+
+#define BDEBUG
+#ifndef BDEBUG
+ return (usal_cmd(usalp));
+#else
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usalp->verbose) {
+ usal_prbytes("WRa Data", xx, sizeof (xx) - usal_getresid(usalp));
+ printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));
+ }
+ return (a_to_u_3_byte(&xx[1]));
+/* return (0);*/
+#endif
+}
+
+#define BI_ONE_BYTE 0xC0
+#define BI_448_BYTE 0x40
+#define BI_APP_CODE 0x10
+
+static int
+buffer_inquiry_teac(SCSI *usalp, int fmt)
+{
+ Uchar xx[448];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->size = 448;
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = SC_BUFFER_INQUIRY;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ if (fmt > 0) {
+ scmd->cdb.g5_cdb.addr[3] = fmt;
+ if (fmt == BI_ONE_BYTE)
+ scmd->size = 1;
+ } else {
+ scmd->cdb.g5_cdb.addr[3] = BI_448_BYTE;
+/* scmd->cdb.g5_cdb.addr[3] = BI_APP_CODE;*/
+ }
+
+ usalp->cmdname = "buffer inquiry";
+
+#define BDEBUG
+#ifndef BDEBUG
+ return (usal_cmd(usalp));
+#else
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usalp->verbose) {
+/* usal_prbytes("WRa Data", xx, sizeof (xx) - usal_getresid(usalp));*/
+/* usal_prbytes("WRa Data", xx, 1);*/
+
+ if (fmt > 0) printf("fmt: %X ", fmt);
+ usal_prbytes("WRa Data", xx, 9);
+ printf("%d\n", xx[8] - xx[1]);
+/* printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
+ }
+ return (0);
+#endif
+}
+
+#ifdef XXBUFFER
+static void
+check_buffer_teac(SCSI *usalp)
+{
+ printf("-------\n");
+ buffer_inquiry_teac(usalp, 0);
+#ifdef SL
+ usleep(40000);
+ buffer_inquiry_teac(usalp, 0);
+#endif
+ read_peak_buffer_cap_teac(usalp);
+}
+#endif
+/*--------------------------------------------------------------------------*/
+#ifdef XXDEBUG
+#include "scsimmc.h"
+
+static int g7_teac(SCSI *usalp);
+static int g6_teac(SCSI *usalp);
+
+static int
+g7_teac(SCSI *usalp)
+{
+ Uchar xx[2048];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g5_cdb.cmd = 0xDf;
+/* scmd->cdb.g5_cdb.cmd = 0xE5;*/
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+/* scmd->cdb.g5_cdb.addr[3] = BI_ONE_BYTE;*/
+/* scmd->size = 1;*/
+
+/* scmd->cdb.g5_cdb.addr[3] = BI_448_BYTE;*/
+/* scmd->cdb.g5_cdb.addr[3] = BI_APP_CODE;*/
+
+ usalp->cmdname = "g7 teac";
+
+/* return (usal_cmd(usalp));*/
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+/* if (usalp->verbose) {*/
+ usal_prbytes("WRa Data", xx, sizeof (xx) - usal_getresid(usalp));
+/* usal_prbytes("WRa Data", xx, 1);*/
+/* usal_prbytes("WRa Data", xx, 9);*/
+/*printf("%d\n", xx[8] - xx[1]);*/
+/* printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
+/* }*/
+ return (0);
+}
+
+static int
+g6_teac(SCSI *usalp)
+{
+ Uchar xx[2048];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)xx, sizeof (xx), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)xx;
+ scmd->size = sizeof (xx);
+ scmd->flags = SCG_RECV_DATA |SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ scmd->cdb.g1_cdb.cmd = 0xC1;
+ scmd->cdb.g1_cdb.cmd = 0xC3;
+ scmd->cdb.g1_cdb.cmd = 0xC6;
+ scmd->cdb.g1_cdb.cmd = 0xC7; /* Read TOC */
+ scmd->cdb.g1_cdb.cmd = 0xCe;
+ scmd->cdb.g1_cdb.cmd = 0xCF;
+ scmd->cdb.g1_cdb.cmd = 0xC7; /* Read TOC */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "g6 teac";
+
+/* return (usal_cmd(usalp));*/
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+/* if (usalp->verbose) {*/
+ usal_prbytes("WRa Data", xx, sizeof (xx) - usal_getresid(usalp));
+/* usal_prbytes("WRa Data", xx, 1);*/
+/* usal_prbytes("WRa Data", xx, 9);*/
+/*printf("%d\n", xx[8] - xx[1]);*/
+/* printf("Buffer cap: %ld\n", a_to_u_3_byte(&xx[1]));*/
+/* }*/
+ return (0);
+}
+
+static void
+xxtest_teac(SCSI *usalp)
+{
+ read_peak_buffer_cap_teac(usalp);
+
+/*#define XDI*/
+#ifdef XDI
+ {
+ Uchar cbuf[512];
+
+/* read_disk_info_teac(usalp, data, length, type)*/
+/* read_disk_info_teac(usalp, cbuf, 512, 2);*/
+/* read_disk_info_teac(usalp, cbuf, 512, 2);*/
+ read_disk_info_teac(usalp, cbuf, 512, 3);
+ usal_prbytes("DI Data", cbuf, sizeof (cbuf) - usal_getresid(usalp));
+ }
+#endif /* XDI */
+
+ buffer_inquiry_teac(usalp, -1);
+
+/*#define XBU*/
+#ifdef XBU
+ {
+ int i;
+
+ for (i = 0; i < 63; i++) {
+ usalp->silent++;
+ buffer_inquiry_teac(usalp, i<<2);
+ usalp->silent--;
+ }
+ }
+#endif /* XBU */
+
+/* printf("LLLL\n");*/
+/* g7_teac(usalp);*/
+/* g6_teac(usalp);*/
+}
+#endif /* XXDEBUG */
diff --git a/wodim/drv_mmc.c b/wodim/drv_mmc.c
new file mode 100644
index 0000000..b075889
--- /dev/null
+++ b/wodim/drv_mmc.c
@@ -0,0 +1,4362 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)drv_mmc.c 1.163 06/01/12 Copyright 1997-2006 J. Schilling */
+/*
+ * CDR device implementation for
+ * SCSI-3/mmc conforming drives
+ * e.g. Yamaha CDR-400, Ricoh MP6200
+ *
+ * Copyright (c) 1997-2006 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*#define DEBUG*/
+#define PRINT_ATIP
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsimmc.h"
+#include "mmcvendor.h"
+#include "wodim.h"
+#include "scsi_scan.h"
+
+extern char *driveropts;
+
+extern int debug;
+extern int lverbose;
+extern int xdebug;
+
+static int curspeed = 1;
+
+static char clv_to_speed[16] = {
+/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static char hs_clv_to_speed[16] = {
+/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 6, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static char us_clv_to_speed[16] = {
+/* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
+ 0, 2, 4, 8, 0, 0, 16, 0, 24, 32, 40, 48, 0, 0, 0, 0
+};
+
+#ifdef __needed__
+static int mmc_load(SCSI *usalp, cdr_t *dp);
+static int mmc_unload(SCSI *usalp, cdr_t *dp);
+#endif
+void mmc_opthelp(cdr_t *dp, int excode);
+char *hasdrvopt(char *optstr, char *optname);
+static cdr_t *identify_mmc(SCSI *usalp, cdr_t *, struct scsi_inquiry *);
+static int attach_mmc(SCSI *usalp, cdr_t *);
+static int attach_mdvd(SCSI *usalp, cdr_t *);
+int check_writemodes_mmc(SCSI *usalp, cdr_t *dp);
+int check_writemodes_mdvd(SCSI *usalp, cdr_t *dp);
+static int deflt_writemodes_mmc(SCSI *usalp, BOOL reset_dummy);
+static int deflt_writemodes_mdvd(SCSI *usalp, BOOL reset_dummy);
+static int get_diskinfo(SCSI *usalp, struct disk_info *dip);
+static void di_to_dstat(struct disk_info *dip, dstat_t *dsp);
+static int get_atip(SCSI *usalp, struct atipinfo *atp);
+#ifdef PRINT_ATIP
+static int get_pma(SCSI *usalp);
+#endif
+static int init_mmc(SCSI *usalp, cdr_t *dp);
+static int getdisktype_mmc(SCSI *usalp, cdr_t *dp);
+static int getdisktype_mdvd(SCSI *usalp, cdr_t *dp);
+static int speed_select_mmc(SCSI *usalp, cdr_t *dp, int *speedp);
+static int speed_select_mdvd(SCSI *usalp, cdr_t *dp, int *speedp);
+static int mmc_set_speed(SCSI *usalp, int readspeed, int writespeed,
+ int rotctl);
+static int next_wr_addr_mmc(SCSI *usalp, track_t *trackp, long *ap);
+static int next_wr_addr_mdvd(SCSI *usalp, track_t *trackp, long *ap);
+static int write_leadin_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int close_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int close_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_session_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_session_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int waitfix_mmc(SCSI *usalp, int secs);
+static int fixate_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int fixate_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int blank_mmc(SCSI *usalp, cdr_t *dp, long addr, int blanktype);
+static int format_mdvd(SCSI *usalp, cdr_t *dp, int formattype);
+static int send_opc_mmc(SCSI *usalp, caddr_t, int cnt, int doopc);
+static int opt1_mmc(SCSI *usalp, cdr_t *dp);
+static int opt1_mdvd(SCSI *usalp, cdr_t *dp);
+static int opt2_mmc(SCSI *usalp, cdr_t *dp);
+static int scsi_sony_write(SCSI *usalp, caddr_t bp, long sectaddr, long size,
+ int blocks, BOOL islast);
+static int gen_cue_mmc(track_t *trackp, void *vcuep, BOOL needgap);
+static void fillcue(struct mmc_cue *cp, int ca, int tno, int idx, int dataform,
+ int scms, msf_t *mp);
+static int send_cue_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int stats_mmc(SCSI *usalp, cdr_t *dp);
+static BOOL mmc_isplextor(SCSI *usalp);
+static BOOL mmc_isyamaha(SCSI *usalp);
+static void do_varirec_plextor(SCSI *usalp);
+static int do_gigarec_plextor(SCSI *usalp);
+static int drivemode_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode,
+ void *modeval);
+static int drivemode2_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode,
+ void *modeval);
+static int check_varirec_plextor(SCSI *usalp);
+static int check_gigarec_plextor(SCSI *usalp);
+static int varirec_plextor(SCSI *usalp, BOOL on, int val);
+static int gigarec_plextor(SCSI *usalp, int val);
+static Int32_t gigarec_mult(int code, Int32_t val);
+static int check_ss_hide_plextor(SCSI *usalp);
+static int check_speed_rd_plextor(SCSI *usalp);
+static int check_powerrec_plextor(SCSI *usalp);
+static int ss_hide_plextor(SCSI *usalp, BOOL do_ss, BOOL do_hide);
+static int speed_rd_plextor(SCSI *usalp, BOOL do_speedrd);
+static int powerrec_plextor(SCSI *usalp, BOOL do_powerrec);
+static int get_speeds_plextor(SCSI *usalp, int *selp, int *maxp, int *lastp);
+static int bpc_plextor(SCSI *usalp, int mode, int *bpp);
+static int set_audiomaster_yamaha(SCSI *usalp, cdr_t *dp, BOOL keep_mode);
+
+struct ricoh_mode_page_30 * get_justlink_ricoh(SCSI *usalp, Uchar *mode);
+static int force_speed_yamaha(SCSI *usalp, int readspeed, int writespeed);
+static BOOL get_tattoo_yamaha(SCSI *usalp, BOOL print, Int32_t *irp,
+ Int32_t *orp);
+static int do_tattoo_yamaha(SCSI *usalp, FILE *f);
+static int yamaha_write_buffer(SCSI *usalp, int mode, int bufferid, long offset,
+ long parlen, void *buffer, long buflen);
+static int dvd_dual_layer_split(SCSI *usalp, cdr_t *dp, long tsize);
+
+extern int reserve_track(SCSI *usalp, Ulong size); /* FIXME */
+extern int scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background); /* FIXME */
+
+#ifdef __needed__
+static int
+mmc_load(SCSI *usalp, cdr_t *dp)
+{
+ return (scsi_load_unload(usalp, 1));
+}
+
+static int
+mmc_unload(SCSI *usalp, cdr_t *dp)
+{
+ return (scsi_load_unload(usalp, 0));
+}
+#endif
+
+/*
+ * MMC CD-writer
+ */
+cdr_t cdr_mmc = {
+ 0, 0,
+ CDR_SWABAUDIO,
+ CDR_CDRW_ALL,
+ 372, 372,
+ "mmc_cdr",
+ "generic SCSI-3/mmc CD-R/CD-RW driver",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ attach_mmc,
+ init_mmc,
+ getdisktype_mmc,
+ scsi_load,
+ scsi_unload,
+ read_buff_cap,
+ cmd_dummy, /* check_recovery */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_mmc,
+ select_secsize,
+ next_wr_addr_mmc,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_cdr_write,
+ gen_cue_mmc,
+ send_cue_mmc,
+ write_leadin_mmc,
+ open_track_mmc,
+ close_track_mmc,
+ open_session_mmc,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ fixate_mmc,
+ stats_mmc,
+ blank_mmc,
+ format_dummy,
+ send_opc_mmc,
+ opt1_mmc,
+ opt2_mmc,
+};
+
+cdr_t cdr_mdvd = {
+ 0, 0,
+ CDR_SWABAUDIO,
+ CDR_CDRW_ALL,
+ 370,370,
+ "mmc_mdvd",
+ "generic SCSI-3/mmc DVD-R(W) driver",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ attach_mdvd,
+ init_mmc,
+ getdisktype_mdvd,
+ scsi_load,
+ scsi_unload,
+ read_buff_cap,
+ cmd_dummy, /* check_recovery */
+ (int(*)__PR((SCSI *, cdr_t *, int)))cmd_dummy, /* recover */
+ speed_select_mdvd,
+ select_secsize,
+ next_wr_addr_mdvd,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_cdr_write,
+ (int(*)__PR((track_t *, void *, BOOL)))cmd_dummy, /* gen_cue */
+ (int(*)__PR((SCSI *usalp, cdr_t *, track_t *)))cmd_dummy, /* send_cue */
+ write_leadin_mmc,
+ open_track_mdvd,
+ close_track_mdvd,
+ open_session_mdvd,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ fixate_mdvd,
+ stats_mmc,
+ blank_mmc,
+ format_mdvd,
+ send_opc_mmc,
+ opt1_mdvd,
+ opt2_mmc,
+ dvd_dual_layer_split,
+};
+
+/*
+ * Sony MMC CD-writer
+ */
+cdr_t cdr_mmc_sony = {
+ 0, 0,
+ CDR_SWABAUDIO,
+ CDR_CDRW_ALL,
+ 372, 372,
+ "mmc_cdr_sony",
+ "generic SCSI-3/mmc CD-R/CD-RW driver (Sony 928 variant)",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ attach_mmc,
+ init_mmc,
+ getdisktype_mmc,
+ scsi_load,
+ scsi_unload,
+ read_buff_cap,
+ cmd_dummy, /* check_recovery */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_mmc,
+ select_secsize,
+ next_wr_addr_mmc,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_sony_write,
+ gen_cue_mmc,
+ send_cue_mmc,
+ write_leadin_mmc,
+ open_track_mmc,
+ close_track_mmc,
+ open_session_mmc,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ fixate_mmc,
+ cmd_dummy, /* stats */
+ blank_mmc,
+ format_dummy,
+ send_opc_mmc,
+ opt1_mmc,
+ opt2_mmc,
+};
+
+/*
+ * SCSI-3/mmc conformant CD-ROM drive
+ */
+cdr_t cdr_cd = {
+ 0, 0,
+ CDR_ISREADER|CDR_SWABAUDIO,
+ CDR_CDRW_NONE,
+ 372, 372,
+ "mmc_cd",
+ "generic SCSI-3/mmc CD-ROM driver",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ attach_mmc,
+ cmd_dummy,
+ drive_getdisktype,
+ scsi_load,
+ scsi_unload,
+ read_buff_cap,
+ cmd_dummy, /* check_recovery */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_mmc,
+ select_secsize,
+ (int(*)(SCSI *usalp, track_t *, long *))cmd_ill, /* next_wr_addr */
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_cdr_write,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_mmc,
+ close_track_mmc,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+/*
+ * Old pre SCSI-3/mmc CD drive
+ */
+cdr_t cdr_oldcd = {
+ 0, 0,
+ CDR_ISREADER,
+ CDR_CDRW_NONE,
+ 372, 372,
+ "scsi2_cd",
+ "generic SCSI-2 CD-ROM driver",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ drive_attach,
+ cmd_dummy,
+ drive_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_dummy,
+ cmd_dummy, /* check_recovery */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_mmc,
+ select_secsize,
+ (int(*)(SCSI *usal, track_t *, long *))cmd_ill, /* next_wr_addr */
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_cdr_write,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_mmc,
+ close_track_mmc,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+/*
+ * SCSI-3/mmc conformant CD or DVD writer
+ * Checks the current medium and then returns either cdr_mmc or cdr_dvd
+ */
+cdr_t cdr_cd_dvd = {
+ 0, 0,
+ CDR_SWABAUDIO,
+ CDR_CDRW_ALL,
+ 372, 372,
+ "mmc_cd_dvd",
+ "generic SCSI-3/mmc CD/DVD driver (checks media)",
+ 0,
+ (dstat_t *)0,
+ identify_mmc,
+ attach_mmc,
+ cmd_dummy,
+ drive_getdisktype,
+ scsi_load,
+ scsi_unload,
+ read_buff_cap,
+ cmd_dummy, /* check_recovery */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_mmc,
+ select_secsize,
+ (int(*)(SCSI *usalp, track_t *, long *))cmd_ill, /* next_wr_addr */
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ scsi_cdr_write,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_mmc,
+ close_track_mmc,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* fixation */
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+void
+mmc_opthelp(cdr_t *dp, int excode)
+{
+ BOOL haveopts = FALSE;
+
+ fprintf(stderr, "Driver options:\n");
+ if (dp->cdr_flags & CDR_BURNFREE) {
+ fprintf(stderr, "burnfree Prepare writer to use BURN-Free technology\n");
+ fprintf(stderr, "noburnfree Disable using BURN-Free technology\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_VARIREC) {
+ fprintf(stderr, "varirec=val Set VariRec Laserpower to -2, -1, 0, 1, 2\n");
+ fprintf(stderr, " Only works for audio and if speed is set to 4\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_GIGAREC) {
+ fprintf(stderr, "gigarec=val Set GigaRec capacity ratio to 0.6, 0.7, 0.8, 1.0, 1.2, 1.3, 1.4\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_AUDIOMASTER) {
+ fprintf(stderr, "audiomaster Turn Audio Master feature on (SAO CD-R Audio/Data only)\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_FORCESPEED) {
+ fprintf(stderr, "forcespeed Tell the drive to force speed even for low quality media\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_SPEEDREAD) {
+ fprintf(stderr, "speedread Tell the drive to read as fast as possible\n");
+ fprintf(stderr, "nospeedread Disable to read as fast as possible\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_DISKTATTOO) {
+ fprintf(stderr, "tattooinfo Print image size info for DiskT@2 feature\n");
+ fprintf(stderr, "tattoofile=name Use 'name' as DiskT@2 image file\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_SINGLESESS) {
+ fprintf(stderr, "singlesession Tell the drive to behave as single session only drive\n");
+ fprintf(stderr, "nosinglesession Disable single session only mode\n");
+ haveopts = TRUE;
+ }
+ if (dp->cdr_flags & CDR_HIDE_CDR) {
+ fprintf(stderr, "hidecdr Tell the drive to hide CD-R media\n");
+ fprintf(stderr, "nohidecdr Disable hiding CD-R media\n");
+ haveopts = TRUE;
+ }
+ if (!haveopts) {
+ fprintf(stderr, "None supported for this drive.\n");
+ }
+ exit(excode);
+}
+
+char *
+hasdrvopt(char *optstr, char *optname)
+{
+ char *ep;
+ char *np;
+ char *ret = NULL;
+ int optnamelen;
+ int optlen;
+ BOOL not = FALSE;
+
+ if (optstr == NULL)
+ return (ret);
+
+ optnamelen = strlen(optname);
+
+ while (*optstr) {
+ not = FALSE; /* Reset before every token */
+ if ((ep = strchr(optstr, ',')) != NULL) {
+ optlen = ep - optstr;
+ np = &ep[1];
+ } else {
+ optlen = strlen(optstr);
+ np = &optstr[optlen];
+ }
+ if ((ep = strchr(optstr, '=')) != NULL) {
+ if (ep < np)
+ optlen = ep - optstr;
+ }
+ if (optstr[0] == '!') {
+ optstr++;
+ optlen--;
+ not = TRUE;
+ }
+ if (strncmp(optstr, "no", 2) == 0) {
+ optstr += 2;
+ optlen -= 2;
+ not = TRUE;
+ }
+ if (strncmp(optstr, optname, optlen) == 0) {
+ ret = &optstr[optlen];
+ break;
+ }
+ optstr = np;
+ }
+ if (ret != NULL) {
+ if (*ret == ',' || *ret == '\0') {
+ if (not)
+ return ("0");
+ return ("1");
+ }
+ if (*ret == '=') {
+ if (not)
+ return (NULL);
+ return (++ret);
+ }
+ }
+ return (ret);
+}
+
+static cdr_t *
+identify_mmc(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip)
+{
+ BOOL cdrr = FALSE; /* Read CD-R */
+ BOOL cdwr = FALSE; /* Write CD-R */
+ BOOL cdrrw = FALSE; /* Read CD-RW */
+ BOOL cdwrw = FALSE; /* Write CD-RW */
+ BOOL dvdwr = FALSE; /* DVD writer */
+ BOOL is_dvd = FALSE; /* use DVD driver*/
+ Uchar mode[0x100];
+ struct cd_mode_page_2A *mp;
+ int profile;
+
+ if (ip->type != INQ_WORM && ip->type != INQ_ROMD)
+ return ((cdr_t *)0);
+
+ allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */
+
+ usalp->silent++;
+ mp = mmc_cap(usalp, mode); /* Get MMC capabilities */
+ usalp->silent--;
+ if (mp == NULL)
+ return (&cdr_oldcd); /* Pre SCSI-3/mmc drive */
+
+ /*
+ * At this point we know that we have a SCSI-3/mmc compliant drive.
+ * Unfortunately ATAPI drives violate the SCSI spec in returning
+ * a response data format of '1' which from the SCSI spec would
+ * tell us not to use the "PF" bit in mode select. As ATAPI drives
+ * require the "PF" bit to be set, we 'correct' the inquiry data.
+ *
+ * XXX xxx_identify() should not have any side_effects ??
+ */
+ if (ip->data_format < 2)
+ ip->data_format = 2;
+
+ /*
+ * First handle exceptions....
+ */
+ if (strncmp(ip->vendor_info, "SONY", 4) == 0 &&
+ strncmp(ip->prod_ident, "CD-R CDU928E", 14) == 0) {
+ return (&cdr_mmc_sony);
+ }
+
+ /*
+ * Now try to do it the MMC-3 way....
+ */
+ profile = get_curprofile(usalp);
+ if (xdebug)
+ printf("Current profile: 0x%04X\n", profile);
+ if (profile == 0) {
+ if (xdebug)
+ print_profiles(usalp);
+ /*
+ * If the current profile is 0x0000, then the
+ * drive does not know about the media. First
+ * close the tray and then try to issue the
+ * get_curprofile() command again.
+ */
+ usalp->silent++;
+ load_media(usalp, dp, FALSE);
+ usalp->silent--;
+ profile = get_curprofile(usalp);
+ scsi_prevent_removal(usalp, 0);
+ if (xdebug)
+ printf("Current profile: 0x%04X\n", profile);
+ }
+ if (profile >= 0) {
+ if (lverbose)
+ print_profiles(usalp);
+ if (profile == 0 || (profile >= 0x10 && profile <= 0x15) || profile > 0x19) {
+ /*
+ * 10h DVD-ROM
+ * 11h DVD-R
+ * 12h DVD-RAM
+ * 13h DVD-RW (Restricted overwrite)
+ * 14h DVD-RW (Sequential recording)
+ * 1Ah DVD+RW
+ * 1Bh DVD+R
+ * 2Bh DVD+R DL
+ *
+ */
+ if (profile == 0x11 || profile == 0x13 || profile == 0x14 || profile == 0x1A || profile == 0x1B || profile == 0x2B) {
+ is_dvd = TRUE;
+ dp = &cdr_mdvd;
+ } else {
+ is_dvd = FALSE;
+ dp = &cdr_cd;
+
+ if (profile == 0) { /* No Medium */
+ BOOL is_cdr = FALSE;
+
+ /*
+ * Check for CD-writer
+ */
+ get_wproflist(usalp, &is_cdr, NULL,
+ NULL, NULL);
+ if (is_cdr)
+ return (&cdr_mmc);
+ /*
+ * Other MMC-3 drive without media
+ */
+ return (dp);
+ } if (profile == 0x12) { /* DVD-RAM */
+ errmsgno(EX_BAD,
+ "Found unsupported DVD-RAM media.\n");
+ return (dp);
+ }
+ }
+ }
+ } else {
+ if (xdebug)
+ printf("Drive is pre MMC-3\n");
+ }
+
+ mmc_getval(mp, &cdrr, &cdwr, &cdrrw, &cdwrw, NULL, &dvdwr);
+
+ if (!cdwr && !cdwrw) { /* SCSI-3/mmc CD drive */
+ /*
+ * If the drive does not support to write CD's, we select the
+ * CD-ROM driver here. If we have DVD-R/DVD-RW support compiled
+ * in, we may later decide to switch to the DVD driver.
+ */
+ dp = &cdr_cd;
+ } else {
+ /*
+ * We need to set the driver to cdr_mmc because we may come
+ * here with driver set to cdr_cd_dvd which is not a driver
+ * that may be used for actual CD/DVD writing.
+ */
+ dp = &cdr_mmc;
+ }
+
+/*#define DVD_DEBUG*/
+#ifdef DVD_DEBUG
+ if (1) { /* Always check for DVD media in debug mode */
+#else
+ if ((cdwr || cdwrw) && dvdwr) {
+#endif
+ char xb[32];
+
+#ifndef DVD_DEBUG
+ usalp->silent++;
+#else
+ fprintf(stderr, "identify_dvd: checking for DVD media\n");
+#endif
+ if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) {
+ /*
+ * If read DVD structure is supported and works, then
+ * we must have a DVD media in the drive. Signal to
+ * use the DVD driver.
+ */
+ is_dvd = TRUE;
+ } else {
+ if (usal_sense_key(usalp) == SC_NOT_READY) {
+ /*
+ * If the SCSI sense key is NOT READY, then the
+ * drive does not know about the media. First
+ * close the tray and then try to issue the
+ * read_dvd_structure() command again.
+ */
+ load_media(usalp, dp, FALSE);
+ if (read_dvd_structure(usalp, (caddr_t)xb, 32, 0, 0, 0) >= 0) {
+ is_dvd = TRUE;
+ }
+ scsi_prevent_removal(usalp, 0);
+ }
+ }
+#ifndef DVD_DEBUG
+ usalp->silent--;
+#else
+ fprintf(stderr, "identify_dvd: is_dvd: %d\n", is_dvd);
+#endif
+ }
+ if (is_dvd) {
+ if(lverbose>2)
+ fprintf(stderr, "Found DVD media: using cdr_mdvd.\n");
+ dp = &cdr_mdvd;
+ }
+ dp->profile = profile;
+ dp->is_dvd = is_dvd;
+ return (dp);
+}
+
+static int
+attach_mmc(SCSI *usalp, cdr_t *dp)
+{
+ int ret;
+ Uchar mode[0x100];
+ struct cd_mode_page_2A *mp;
+ struct ricoh_mode_page_30 *rp = NULL;
+
+ allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */
+
+ usalp->silent++;
+ mp = mmc_cap(usalp, NULL); /* Get MMC capabilities in allocated mp */
+ usalp->silent--;
+ if (mp == NULL)
+ return (-1); /* Pre SCSI-3/mmc drive */
+
+ dp->cdr_cdcap = mp; /* Store MMC cap pointer */
+
+ dp->cdr_dstat->ds_dr_max_rspeed = a_to_u_2_byte(mp->max_read_speed)/176;
+ if (dp->cdr_dstat->ds_dr_max_rspeed == 0)
+ dp->cdr_dstat->ds_dr_max_rspeed = 372;
+ dp->cdr_dstat->ds_dr_cur_rspeed = a_to_u_2_byte(mp->cur_read_speed)/176;
+ if (dp->cdr_dstat->ds_dr_cur_rspeed == 0)
+ dp->cdr_dstat->ds_dr_cur_rspeed = 372;
+
+ dp->cdr_dstat->ds_dr_max_wspeed = a_to_u_2_byte(mp->max_write_speed)/176;
+ if (mp->p_len >= 28)
+ dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->v3_cur_write_speed)/176;
+ else
+ dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->cur_write_speed)/176;
+
+ if (dp->cdr_speedmax > dp->cdr_dstat->ds_dr_max_wspeed)
+ dp->cdr_speedmax = dp->cdr_dstat->ds_dr_max_wspeed;
+
+ if (dp->cdr_speeddef > dp->cdr_speedmax)
+ dp->cdr_speeddef = dp->cdr_speedmax;
+
+ rp = get_justlink_ricoh(usalp, mode);
+
+ if (mp->p_len >= 28)
+ dp->cdr_flags |= CDR_MMC3;
+ if (mp->p_len >= 24)
+ dp->cdr_flags |= CDR_MMC2;
+ dp->cdr_flags |= CDR_MMC;
+
+ if (mp->loading_type == LT_TRAY)
+ dp->cdr_flags |= CDR_TRAYLOAD;
+ else if (mp->loading_type == LT_CADDY)
+ dp->cdr_flags |= CDR_CADDYLOAD;
+
+ if (mp->BUF != 0) {
+ dp->cdr_flags |= CDR_BURNFREE;
+ } else if (rp) {
+ if ((dp->cdr_cmdflags & F_DUMMY) && rp->TWBFS && rp->BUEFS)
+ dp->cdr_flags |= CDR_BURNFREE;
+
+ if (rp->BUEFS)
+ dp->cdr_flags |= CDR_BURNFREE;
+ }
+
+ if (mmc_isplextor(usalp)) {
+ if (check_varirec_plextor(usalp) >= 0)
+ dp->cdr_flags |= CDR_VARIREC;
+
+ if (check_gigarec_plextor(usalp) >= 0)
+ dp->cdr_flags |= CDR_GIGAREC;
+
+ if (check_ss_hide_plextor(usalp) >= 0)
+ dp->cdr_flags |= CDR_SINGLESESS|CDR_HIDE_CDR;
+
+ if (check_powerrec_plextor(usalp) >= 0)
+ dp->cdr_flags |= CDR_FORCESPEED;
+
+ if (check_speed_rd_plextor(usalp) >= 0)
+ dp->cdr_flags |= CDR_SPEEDREAD;
+ }
+ if (mmc_isyamaha(usalp)) {
+ if (set_audiomaster_yamaha(usalp, dp, FALSE) >= 0)
+ dp->cdr_flags |= CDR_AUDIOMASTER;
+
+ /*
+ * Starting with CRW 2200 / CRW 3200
+ */
+ if ((mp->p_len+2) >= (unsigned)28)
+ dp->cdr_flags |= CDR_FORCESPEED;
+
+ if (get_tattoo_yamaha(usalp, FALSE, 0, 0))
+ dp->cdr_flags |= CDR_DISKTATTOO;
+ }
+
+ if (rp && rp->AWSCS)
+ dp->cdr_flags |= CDR_FORCESPEED;
+
+#ifdef FUTURE_ROTCTL
+ if (mp->p_len >= 28) {
+ int val;
+
+ val = dp->cdr_dstat->ds_dr_cur_wspeed;
+ if (val == 0)
+ val = 372;
+
+ usalp->verbose++;
+ if (scsi_set_speed(usalp, -1, val, ROTCTL_CAV) < 0) {
+ fprintf(stderr, "XXX\n");
+ }
+ usalp->verbose--;
+ }
+#endif
+
+ check_writemodes_mmc(usalp, dp);
+
+ /* Enable Burnfree by default, it can be disabled later */
+ if ((dp->cdr_flags & CDR_BURNFREE) != 0)
+ dp->cdr_dstat->ds_cdrflags |= RF_BURNFREE;
+
+ if (driveropts != NULL) {
+ char *p;
+
+ if (strcmp(driveropts, "help") == 0) {
+ mmc_opthelp(dp, 0);
+ }
+
+ p = hasdrvopt(driveropts, "varirec");
+ if (p != NULL && (dp->cdr_flags & CDR_VARIREC) != 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_VARIREC;
+ }
+
+ p = hasdrvopt(driveropts, "gigarec");
+ if (p != NULL && (dp->cdr_flags & CDR_GIGAREC) != 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_GIGAREC;
+ }
+
+ p = hasdrvopt(driveropts, "audiomaster");
+ if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_AUDIOMASTER) != 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_AUDIOMASTER;
+ dp->cdr_dstat->ds_cdrflags &= ~RF_BURNFREE;
+ }
+ p = hasdrvopt(driveropts, "forcespeed");
+ if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_FORCESPEED) != 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_FORCESPEED;
+ }
+ p = hasdrvopt(driveropts, "tattooinfo");
+ if (p != NULL && *p == '1' && (dp->cdr_flags & CDR_DISKTATTOO) != 0) {
+ get_tattoo_yamaha(usalp, TRUE, 0, 0);
+ }
+ p = hasdrvopt(driveropts, "tattoofile");
+ if (p != NULL && (dp->cdr_flags & CDR_DISKTATTOO) != 0) {
+ FILE *f;
+
+ if ((f = fileopen(p, "rb")) == NULL)
+ comerr("Cannot open '%s'.\n", p);
+
+ if (do_tattoo_yamaha(usalp, f) < 0)
+ errmsgno(EX_BAD, "Cannot do DiskT@2.\n");
+ fclose(f);
+ }
+ p = hasdrvopt(driveropts, "singlesession");
+ if (p != NULL && (dp->cdr_flags & CDR_SINGLESESS) != 0) {
+ if (*p == '1') {
+ dp->cdr_dstat->ds_cdrflags |= RF_SINGLESESS;
+ } else if (*p == '0') {
+ dp->cdr_dstat->ds_cdrflags &= ~RF_SINGLESESS;
+ }
+ }
+ p = hasdrvopt(driveropts, "hidecdr");
+ if (p != NULL && (dp->cdr_flags & CDR_HIDE_CDR) != 0) {
+ if (*p == '1') {
+ dp->cdr_dstat->ds_cdrflags |= RF_HIDE_CDR;
+ } else if (*p == '0') {
+ dp->cdr_dstat->ds_cdrflags &= ~RF_HIDE_CDR;
+ }
+ }
+ p = hasdrvopt(driveropts, "speedread");
+ if (p != NULL && (dp->cdr_flags & CDR_SPEEDREAD) != 0) {
+ if (*p == '1') {
+ dp->cdr_dstat->ds_cdrflags |= RF_SPEEDREAD;
+ } else if (*p == '0') {
+ dp->cdr_dstat->ds_cdrflags &= ~RF_SPEEDREAD;
+ }
+ }
+ }
+
+ if ((ret = get_supported_cdrw_media_types(usalp)) < 0) {
+ dp->cdr_cdrw_support = CDR_CDRW_ALL;
+ return (0);
+ }
+ dp->cdr_cdrw_support = ret;
+ if (lverbose > 1)
+ printf("Supported CD-RW media types: %02X\n", dp->cdr_cdrw_support);
+
+ return (0);
+}
+
+static int
+attach_mdvd(SCSI *usalp, cdr_t *dp)
+{
+ struct cd_mode_page_2A *mp;
+
+
+ allow_atapi(usalp, TRUE);/* Try to switch to 10 byte mode cmds */
+
+ usalp->silent++;
+ mp = mmc_cap(usalp, NULL);/* Get MMC capabilities in allocated mp */
+ usalp->silent--;
+ if (mp == NULL)
+ return (-1); /* Pre SCSI-3/mmc drive */
+
+ dp->cdr_cdcap = mp; /* Store MMC cap pointer */
+
+ dp->cdr_dstat->ds_dr_max_rspeed = a_to_u_2_byte(mp->max_read_speed)/1385;
+ if (dp->cdr_dstat->ds_dr_max_rspeed == 0)
+ dp->cdr_dstat->ds_dr_max_rspeed = 1385;
+ dp->cdr_dstat->ds_dr_cur_rspeed = a_to_u_2_byte(mp->cur_read_speed)/1385;
+ if (dp->cdr_dstat->ds_dr_cur_rspeed == 0)
+ dp->cdr_dstat->ds_dr_cur_rspeed = 1385;
+
+ dp->cdr_dstat->ds_dr_max_wspeed = a_to_u_2_byte(mp->max_write_speed)/1385;
+ if (mp->p_len >= 28)
+ dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->v3_cur_write_speed)/1385;
+ else
+ dp->cdr_dstat->ds_dr_cur_wspeed = a_to_u_2_byte(mp->cur_write_speed)/1385;
+
+ if (dp->cdr_speedmax > dp->cdr_dstat->ds_dr_max_wspeed)
+ dp->cdr_speedmax = dp->cdr_dstat->ds_dr_max_wspeed;
+
+ if (dp->cdr_speeddef > dp->cdr_speedmax)
+ dp->cdr_speeddef = dp->cdr_speedmax;
+
+
+ if (mp->loading_type == LT_TRAY)
+ dp->cdr_flags |= CDR_TRAYLOAD;
+ else if (mp->loading_type == LT_CADDY)
+ dp->cdr_flags |= CDR_CADDYLOAD;
+
+ if (mp->BUF != 0)
+ dp->cdr_flags |= CDR_BURNFREE;
+
+ check_writemodes_mdvd(usalp, dp);
+
+ if (driveropts != NULL) {
+ if (strcmp(driveropts, "help") == 0) {
+ mmc_opthelp(dp, 0);
+ }
+ }
+
+ return (0);
+}
+
+int
+check_writemodes_mmc(SCSI *usalp, cdr_t *dp)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ if (xdebug)
+ printf("Checking possible write modes: ");
+
+ /*
+ * Reset mp->test_write (-dummy) here.
+ */
+ deflt_writemodes_mmc(usalp, TRUE);
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ usalp->silent--;
+ return (-1);
+ }
+ if (len == 0) {
+ usalp->silent--;
+ return (-1);
+ }
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+
+ /*
+ * mp->test_write has already been reset in deflt_writemodes_mmc()
+ * Do not reset mp->test_write (-dummy) here. It should be set
+ * only at one place and only one time.
+ */
+
+ mp->write_type = WT_TAO;
+ mp->track_mode = TM_DATA;
+ mp->dbtype = DB_ROM_MODE1;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_TAO;
+ if (xdebug)
+ printf("TAO ");
+ } else
+ dp->cdr_flags &= ~CDR_TAO;
+
+ mp->write_type = WT_PACKET;
+ mp->track_mode |= TM_INCREMENTAL;
+/* mp->fp = (trackp->pktsize > 0) ? 1 : 0;*/
+/* i_to_4_byte(mp->packet_size, trackp->pktsize);*/
+ mp->fp = 0;
+ i_to_4_byte(mp->packet_size, 0);
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_PACKET;
+ if (xdebug)
+ printf("PACKET ");
+ } else
+ dp->cdr_flags &= ~CDR_PACKET;
+ mp->fp = 0;
+ i_to_4_byte(mp->packet_size, 0);
+ mp->track_mode = TM_DATA;
+ mp->write_type = WT_SAO;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_SAO;
+ if (xdebug)
+ printf("SAO ");
+ } else
+ dp->cdr_flags &= ~CDR_SAO;
+
+ if (dp->cdr_flags & CDR_SAO) {
+ mp->dbtype = DB_RAW_PQ;
+
+#ifdef __needed__
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_SRAW16;
+ if (xdebug)
+ printf("SAO/R16 ");
+ }
+#endif
+
+ mp->dbtype = DB_RAW_PW;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_SRAW96P;
+ if (xdebug)
+ printf("SAO/R96P ");
+ }
+
+ mp->dbtype = DB_RAW_PW_R;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_SRAW96R;
+ if (xdebug)
+ printf("SAO/R96R ");
+ }
+ }
+
+ mp->write_type = WT_RAW;
+ mp->dbtype = DB_RAW_PQ;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_RAW;
+ dp->cdr_flags |= CDR_RAW16;
+ if (xdebug)
+ printf("RAW/R16 ");
+ }
+
+ mp->dbtype = DB_RAW_PW;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_RAW;
+ dp->cdr_flags |= CDR_RAW96P;
+ if (xdebug)
+ printf("RAW/R96P ");
+ }
+
+ mp->dbtype = DB_RAW_PW_R;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_RAW;
+ dp->cdr_flags |= CDR_RAW96R;
+ if (xdebug)
+ printf("RAW/R96R ");
+ }
+
+ if (xdebug)
+ printf("\n");
+
+ /*
+ * Reset mp->test_write (-dummy) here.
+ */
+ deflt_writemodes_mmc(usalp, TRUE);
+ usalp->silent--;
+
+ return (0);
+}
+
+int
+check_writemodes_mdvd(SCSI *usalp, cdr_t *dp)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ if (xdebug)
+ printf("Checking possible write modes: ");
+
+ deflt_writemodes_mdvd(usalp, FALSE);
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x05, "DVD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ usalp->silent--;
+ return (-1);
+ }
+ if (len == 0) {
+ usalp->silent--;
+ return (-1);
+ }
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof(struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ mp->test_write = 0;
+
+ /*We only check for PACKET and SAO since these are the only supported modes for DVD */
+ /*XXX these checks are irrelevant because they are not medium sensitive. ie the device returns
+ error only when it does not support a given mode for ALL mediums. It should check using
+ GET CONFIGURATION command.*/
+
+ mp->write_type = WT_PACKET;
+ mp->fp = 0;
+ i_to_4_byte(mp->packet_size, 0);
+
+ if (set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_PACKET;
+ if (xdebug)
+ printf("PACKET ");
+ } else
+ dp->cdr_flags &= ~CDR_PACKET;
+ mp->fp = 0;
+ i_to_4_byte(mp->packet_size, 0);
+ mp->track_mode = TM_DATA;
+
+
+ mp->write_type = WT_SAO;
+
+ if (set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ dp->cdr_flags |= CDR_SAO;
+ if (xdebug)
+ printf("SAO ");
+ } else
+ dp->cdr_flags &= ~CDR_SAO;
+
+
+ if (xdebug)
+ printf("\n");
+
+ deflt_writemodes_mdvd(usalp, TRUE);
+ usalp->silent--;
+ return (0);
+}
+
+static int
+deflt_writemodes_mmc(SCSI *usalp, BOOL reset_dummy)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ usalp->silent--;
+ return (-1);
+ }
+ if (len == 0) {
+ usalp->silent--;
+ return (-1);
+ }
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+ fprintf(stderr, "Audio pause len: %d\n", a_to_2_byte(mp->audio_pause_len));
+#endif
+
+ /*
+ * This is the only place where we reset mp->test_write (-dummy)
+ */
+ if (reset_dummy)
+ mp->test_write = 0;
+
+ /*
+ * Set default values:
+ * Write type = 01 (track at once)
+ * Track mode = 04 (CD-ROM)
+ * Data block type = 08 (CD-ROM)
+ * Session format = 00 (CD-ROM)
+ *
+ * XXX Note: the same code appears in check_writemodes_mmc() and
+ * XXX in speed_select_mmc().
+ */
+ mp->write_type = WT_TAO;
+ mp->track_mode = TM_DATA;
+ mp->dbtype = DB_ROM_MODE1;
+ mp->session_format = SES_DA_ROM; /* Matsushita has illegal def. value */
+
+ i_to_2_byte(mp->audio_pause_len, 150); /* LG has illegal def. value */
+
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+
+ mp->write_type = WT_SAO;
+ mp->LS_V = 0;
+ mp->copy = 0;
+ mp->fp = 0;
+ mp->multi_session = MS_NONE;
+ mp->host_appl_code = 0;
+
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1)) {
+ usalp->silent--;
+ return (-1);
+ }
+ }
+ usalp->silent--;
+ return (0);
+}
+
+static int
+deflt_writemodes_mdvd(SCSI *usalp, BOOL reset_dummy)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x05, "DVD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ usalp->silent--;
+ return (-1);
+ }
+ if (len == 0) {
+ usalp->silent--;
+ return (-1);
+ }
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof(struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ mp->test_write = 0;
+ /*
+ * This is the only place where we reset mp->test_write (-dummy) for DVD
+ */
+ if (reset_dummy)
+ mp->test_write = 0;
+
+ /*
+ * Set default values:
+ * Write type = 02 (session at once)
+ *
+ * XXX Note: the same code appears in check_writemodes_mmc() and
+ * XXX in speed_select_mmc().
+ */
+ mp->write_type = WT_SAO;
+ mp->track_mode = TM_DATA;
+ mp->dbtype = DB_ROM_MODE1;
+ mp->session_format = SES_DA_ROM;
+
+
+ if (set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+ return (0);
+}
+
+#ifdef PRINT_ATIP
+static void print_di(struct disk_info *dip);
+static void atip_printspeed(char *fmt, int speedindex, char speedtab[]);
+static void print_atip(SCSI *usalp, struct atipinfo *atp);
+#endif /* PRINT_ATIP */
+
+static int
+get_diskinfo(SCSI *usalp, struct disk_info *dip)
+{
+ int len;
+ int ret;
+
+ fillbytes((caddr_t)dip, sizeof (*dip), '\0');
+
+ /*
+ * Used to be 2 instead of 4 (now). But some Y2k ATAPI drives as used
+ * by IOMEGA create a DMA overrun if we try to transfer only 2 bytes.
+ */
+/* if (read_disk_info(usalp, (caddr_t)dip, 2) < 0)*/
+ if (read_disk_info(usalp, (caddr_t)dip, 4) < 0)
+ return (-1);
+ len = a_to_u_2_byte(dip->data_len);
+ len += 2;
+ ret = read_disk_info(usalp, (caddr_t)dip, len);
+
+#ifdef DEBUG
+ usal_prbytes("Disk info:", (Uchar *)dip,
+ len-usal_getresid(usalp));
+#endif
+ return (ret);
+}
+
+static void
+di_to_dstat(struct disk_info *dip, dstat_t *dsp)
+{
+ dsp->ds_diskid = a_to_u_4_byte(dip->disk_id);
+ if (dip->did_v)
+ dsp->ds_flags |= DSF_DID_V;
+ dsp->ds_disktype = dip->disk_type;
+ dsp->ds_diskstat = dip->disk_status;
+ dsp->ds_sessstat = dip->sess_status;
+ if (dip->erasable)
+ dsp->ds_flags |= DSF_ERA;
+
+ dsp->ds_trfirst = dip->first_track;
+ dsp->ds_trlast = dip->last_track_ls;
+ dsp->ds_trfirst_ls = dip->first_track_ls;
+
+ dsp->ds_maxblocks = msf_to_lba(dip->last_lead_out[1],
+ dip->last_lead_out[2],
+ dip->last_lead_out[3], TRUE);
+ /*
+ * Check for 0xFF:0xFF/0xFF which is an indicator for a complete disk
+ */
+ if (dsp->ds_maxblocks == 1166730)
+ dsp->ds_maxblocks = -1L;
+
+ dsp->ds_first_leadin = msf_to_lba(dip->last_lead_in[1],
+ dip->last_lead_in[2],
+ dip->last_lead_in[3], FALSE);
+ if (dsp->ds_first_leadin > 0)
+ dsp->ds_first_leadin = 0;
+
+ if (dsp->ds_last_leadout == 0 && dsp->ds_maxblocks >= 0)
+ dsp->ds_last_leadout = dsp->ds_maxblocks;
+ dsp->ds_trfirst=dip->first_track;
+ dsp->ds_trlast=dip->last_track_ls;
+ dsp->ds_trfirst_ls=dip->first_track_ls;
+}
+
+static int
+get_atip(SCSI *usalp, struct atipinfo *atp)
+{
+ int len;
+ int ret;
+
+ fillbytes((caddr_t)atp, sizeof (*atp), '\0');
+
+ /*
+ * Used to be 2 instead of sizeof (struct tocheader), but all
+ * other places in the code use sizeof (struct tocheader) too and
+ * some Y2k ATAPI drives as used by IOMEGA create a DMA overrun if we
+ * try to transfer only 2 bytes.
+ */
+ if (read_toc(usalp, (caddr_t)atp, 0, sizeof (struct tocheader), 0, FMT_ATIP) < 0)
+ return (-1);
+ len = a_to_u_2_byte(atp->hd.len);
+ len += 2;
+ ret = read_toc(usalp, (caddr_t)atp, 0, len, 0, FMT_ATIP);
+
+#ifdef DEBUG
+ usal_prbytes("ATIP info:", (Uchar *)atp,
+ len-usal_getresid(usalp));
+#endif
+ /*
+ * Yamaha sometimes returns zeroed ATIP info for disks without ATIP
+ */
+ if (atp->desc.lead_in[1] == 0 &&
+ atp->desc.lead_in[2] == 0 &&
+ atp->desc.lead_in[3] == 0 &&
+ atp->desc.lead_out[1] == 0 &&
+ atp->desc.lead_out[2] == 0 &&
+ atp->desc.lead_out[3] == 0)
+ return (-1);
+
+ if (atp->desc.lead_in[1] >= 0x90 && debug) {
+ /*
+ * Only makes sense with buggy Ricoh firmware.
+ */
+ errmsgno(EX_BAD, "Converting ATIP from BCD\n");
+ atp->desc.lead_in[1] = from_bcd(atp->desc.lead_in[1]);
+ atp->desc.lead_in[2] = from_bcd(atp->desc.lead_in[2]);
+ atp->desc.lead_in[3] = from_bcd(atp->desc.lead_in[3]);
+
+ atp->desc.lead_out[1] = from_bcd(atp->desc.lead_out[1]);
+ atp->desc.lead_out[2] = from_bcd(atp->desc.lead_out[2]);
+ atp->desc.lead_out[3] = from_bcd(atp->desc.lead_out[3]);
+ }
+
+ return (ret);
+}
+
+#ifdef PRINT_ATIP
+
+static int
+get_pma(SCSI *usalp)
+{
+ int len;
+ int ret;
+ char atp[1024];
+
+ fillbytes((caddr_t)atp, sizeof (*atp), '\0');
+
+ /*
+ * Used to be 2 instead of sizeof (struct tocheader), but all
+ * other places in the code use sizeof (struct tocheader) too and
+ * some Y2k ATAPI drives as used by IOMEGA create a DMA overrun if we
+ * try to transfer only 2 bytes.
+ */
+/* if (read_toc(usalp, (caddr_t)atp, 0, 2, 1, FMT_PMA) < 0)*/
+/* if (read_toc(usalp, (caddr_t)atp, 0, 2, 0, FMT_PMA) < 0)*/
+ if (read_toc(usalp, (caddr_t)atp, 0, sizeof (struct tocheader), 0, FMT_PMA) < 0)
+ return (-1);
+/* len = a_to_u_2_byte(atp->hd.len);*/
+ len = a_to_u_2_byte(atp);
+ len += 2;
+/* ret = read_toc(usalp, (caddr_t)atp, 0, len, 1, FMT_PMA);*/
+ ret = read_toc(usalp, (caddr_t)atp, 0, len, 0, FMT_PMA);
+
+#ifdef DEBUG
+ usal_prbytes("PMA:", (Uchar *)atp,
+ len-usal_getresid(usalp));
+#endif
+ ret = read_toc(usalp, (caddr_t)atp, 0, len, 1, FMT_PMA);
+
+#ifdef DEBUG
+ usal_prbytes("PMA:", (Uchar *)atp,
+ len-usal_getresid(usalp));
+#endif
+ return (ret);
+}
+
+#endif /* PRINT_ATIP */
+
+static int
+init_mmc(SCSI *usalp, cdr_t *dp)
+{
+ return (speed_select_mmc(usalp, dp, NULL));
+}
+
+static int
+getdisktype_mdvd(SCSI *usalp, cdr_t *dp)
+{
+ int ret = 0;
+ dstat_t *dsp = dp->cdr_dstat;
+
+ struct track_info track_info;
+ if(lverbose)
+ printf("HINT: use dvd+rw-mediainfo from dvd+rw-tools for information extraction.\n");
+ /* if(getdisktype_mmc(usalp, dp)<0)
+ return -1;
+ */
+
+ /* read rzone info to get the space left on disk */
+ /*ds_trlast is the last rzone on disk, can be invisible */
+ if(read_rzone_info(usalp, (caddr_t)&track_info, sizeof(track_info))>=0)
+ dsp->ds_maxblocks=a_to_u_4_byte(track_info.free_blocks)+a_to_4_byte(track_info.next_writable_addr);
+
+ dsp->ds_disktype&= ~DT_CD;
+ dsp->ds_disktype|= DT_DVD;
+
+ return (ret);
+
+}
+
+static int
+getdisktype_mmc(SCSI *usalp, cdr_t *dp)
+{
+extern char *buf;
+ dstat_t *dsp = dp->cdr_dstat;
+ struct disk_info *dip;
+ Uchar mode[0x100];
+ msf_t msf;
+ BOOL did_atip = FALSE;
+ BOOL did_dummy = FALSE;
+ int rplus;
+
+ msf.msf_min = msf.msf_sec = msf.msf_frame = 0;
+
+ /*
+ * It seems that there are drives that do not support to
+ * read ATIP (e.g. HP 7100)
+ * Also if a NON CD-R media is inserted, this will never work.
+ * For this reason, make a failure non-fatal.
+ */
+ usalp->silent++;
+ if (get_atip(usalp, (struct atipinfo *)mode) >= 0) {
+ struct atipinfo *atp = (struct atipinfo *)mode;
+
+ msf.msf_min = mode[8];
+ msf.msf_sec = mode[9];
+ msf.msf_frame = mode[10];
+ if (atp->desc.erasable) {
+ dsp->ds_flags |= DSF_ERA;
+ if (atp->desc.sub_type == 1)
+ dsp->ds_flags |= DSF_HIGHSP_ERA;
+ else if (atp->desc.sub_type == 2)
+ dsp->ds_flags |= DSF_ULTRASP_ERA;
+ else if (atp->desc.sub_type == 3)
+ dsp->ds_flags |= DSF_ULTRASP_ERA | DSF_ULTRASPP_ERA;
+ }
+ if (atp->desc.a1_v) {
+ if (atp->desc.clv_low != 0)
+ dsp->ds_at_min_speed = clv_to_speed[atp->desc.clv_low];
+ if (atp->desc.clv_high != 0)
+ dsp->ds_at_max_speed = clv_to_speed[atp->desc.clv_high];
+
+ if (atp->desc.erasable && atp->desc.sub_type == 1) {
+ if (atp->desc.clv_high != 0)
+ dsp->ds_at_max_speed = hs_clv_to_speed[atp->desc.clv_high];
+ }
+ }
+ if (atp->desc.a2_v && atp->desc.erasable && (atp->desc.sub_type == 2 || atp->desc.sub_type == 3)) {
+ Uint vlow;
+ Uint vhigh;
+
+ vlow = (atp->desc.a2[0] >> 4) & 0x07;
+ vhigh = atp->desc.a2[0] & 0x0F;
+ if (vlow != 0)
+ dsp->ds_at_min_speed = us_clv_to_speed[vlow];
+ if (vhigh != 0)
+ dsp->ds_at_max_speed = us_clv_to_speed[vhigh];
+ }
+ did_atip = TRUE;
+ }
+ usalp->silent--;
+
+#ifdef PRINT_ATIP
+ if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && did_atip) {
+ print_atip(usalp, (struct atipinfo *)mode);
+ pr_manufacturer(&msf,
+ ((struct atipinfo *)mode)->desc.erasable,
+ ((struct atipinfo *)mode)->desc.uru);
+ }
+#endif
+again:
+ dip = (struct disk_info *)buf;
+ if (get_diskinfo(usalp, dip) < 0)
+ return (-1);
+
+ /*
+ * Check for non writable disk first.
+ */
+
+ /* DVD+RW does not need to be blanked */
+ rplus = dsp->ds_cdrflags;
+ if (dp->profile == 0x1A) rplus = RF_BLANK;
+
+ if (dip->disk_status == DS_COMPLETE &&
+ (rplus & dsp->ds_cdrflags & (RF_WRITE|RF_BLANK)) == RF_WRITE) {
+ if (!did_dummy) {
+ int xspeed = 0xFFFF;
+ int oflags = dp->cdr_cmdflags;
+
+ /*
+ * Try to clear the dummy bit to reset the virtual
+ * drive status. Not all drives support it even though
+ * it is mentioned in the MMC standard.
+ */
+ if (lverbose)
+ printf("Trying to clear drive status.\n");
+
+ dp->cdr_cmdflags &= ~F_DUMMY;
+ speed_select_mmc(usalp, dp, &xspeed);
+ dp->cdr_cmdflags = oflags;
+ did_dummy = TRUE;
+ goto again;
+ }
+ /*
+ * Trying to clear drive status did not work...
+ */
+ reload_media(usalp, dp);
+ }
+ if (get_diskinfo(usalp, dip) < 0)
+ return (-1);
+ di_to_dstat(dip, dsp);
+ if (!did_atip && dsp->ds_first_leadin < 0)
+ lba_to_msf(dsp->ds_first_leadin, &msf);
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && !did_atip) {
+ print_min_atip(dsp->ds_first_leadin, dsp->ds_last_leadout);
+ if (dsp->ds_first_leadin < 0)
+ pr_manufacturer(&msf,
+ dip->erasable,
+ dip->uru);
+ }
+ dsp->ds_maxrblocks = disk_rcap(&msf, dsp->ds_maxblocks,
+ dip->erasable,
+ dip->uru);
+
+
+#ifdef PRINT_ATIP
+#ifdef DEBUG
+ if (get_atip(usalp, (struct atipinfo *)mode) < 0)
+ return (-1);
+ /*
+ * Get pma gibt Ärger mit CW-7502
+ * Wenn Die Disk leer ist, dann stuerzt alles ab.
+ * Firmware 4.02 kann nicht get_pma
+ */
+ if (dip->disk_status != DS_EMPTY) {
+/* get_pma();*/
+ }
+ printf("ATIP lead in: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(mode[8], mode[9], mode[10], FALSE),
+ mode[8], mode[9], mode[10]);
+ printf("ATIP lead out: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(mode[12], mode[13], mode[14], TRUE),
+ mode[12], mode[13], mode[14]);
+ print_di(dip);
+ print_atip(usalp, (struct atipinfo *)mode);
+#endif
+#endif /* PRINT_ATIP */
+ return (drive_getdisktype(usalp, dp));
+}
+
+#ifdef PRINT_ATIP
+
+#define DOES(what, flag) printf(" Does %s%s\n", flag?"":"not ", what);
+#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what);
+#define VAL(what, val) printf(" %s: %d\n", what, val[0]*256 + val[1]);
+#define SVAL(what, val) printf(" %s: %s\n", what, val);
+
+static void
+print_di(struct disk_info *dip)
+{
+ static char *ds_name[] = { "empty", "incomplete/appendable", "complete", "illegal" };
+ static char *ss_name[] = { "empty", "incomplete/appendable", "illegal", "complete", };
+
+ IS("erasable", dip->erasable);
+ printf("disk status: %s\n", ds_name[dip->disk_status]);
+ printf("session status: %s\n", ss_name[dip->sess_status]);
+ printf("first track: %d number of sessions: %d first track in last sess: %d last track in last sess: %d\n",
+ dip->first_track,
+ dip->numsess,
+ dip->first_track_ls,
+ dip->last_track_ls);
+ IS("unrestricted", dip->uru);
+ printf("Disk type: ");
+ switch (dip->disk_type) {
+
+ case SES_DA_ROM: printf("CD-DA or CD-ROM"); break;
+ case SES_CDI: printf("CDI"); break;
+ case SES_XA: printf("CD-ROM XA"); break;
+ case SES_UNDEF: printf("undefined"); break;
+ default: printf("reserved"); break;
+ }
+ printf("\n");
+ if (dip->did_v)
+ printf("Disk id: 0x%lX\n", a_to_u_4_byte(dip->disk_id));
+
+ printf("last start of lead in: %ld\n",
+ msf_to_lba(dip->last_lead_in[1],
+ dip->last_lead_in[2],
+ dip->last_lead_in[3], FALSE));
+ printf("last start of lead out: %ld\n",
+ msf_to_lba(dip->last_lead_out[1],
+ dip->last_lead_out[2],
+ dip->last_lead_out[3], TRUE));
+
+ if (dip->dbc_v)
+ printf("Disk bar code: 0x%lX%lX\n",
+ a_to_u_4_byte(dip->disk_barcode),
+ a_to_u_4_byte(&dip->disk_barcode[4]));
+
+ if (dip->num_opc_entries > 0) {
+ printf("OPC table:\n");
+ }
+}
+
+char *cdr_subtypes[] = {
+ "Normal Rewritable (CLV) media",
+ "High speed Rewritable (CAV) media",
+ "Medium Type A, low Beta category (A-)",
+ "Medium Type A, high Beta category (A+)",
+ "Medium Type B, low Beta category (B-)",
+ "Medium Type B, high Beta category (B+)",
+ "Medium Type C, low Beta category (C-)",
+ "Medium Type C, high Beta category (C+)",
+};
+
+char *cdrw_subtypes[] = {
+ "Normal Rewritable (CLV) media",
+ "High speed Rewritable (CAV) media",
+ "Ultra High speed Rewritable media",
+ "Ultra High speed+ Rewritable media",
+ "Medium Type B, low Beta category (B-)",
+ "Medium Type B, high Beta category (B+)",
+ "Medium Type C, low Beta category (C-)",
+ "Medium Type C, high Beta category (C+)",
+};
+
+static void
+atip_printspeed(char *fmt, int speedindex, char speedtab[])
+{
+ printf("%s:", fmt);
+ if (speedtab[speedindex] == 0) {
+ printf(" %2d (reserved val %2d)",
+ speedtab[speedindex], speedindex);
+ } else {
+ printf(" %2d", speedtab[speedindex]);
+ }
+}
+
+static void
+print_atip(SCSI *usalp, struct atipinfo *atp)
+{
+ char *sub_type;
+ char *speedvtab = clv_to_speed;
+
+ if (usalp->verbose)
+ usal_prbytes("ATIP info: ", (Uchar *)atp, sizeof (*atp));
+
+ printf("ATIP info from disk:\n");
+ printf(" Indicated writing power: %d\n", atp->desc.ind_wr_power);
+ if (atp->desc.erasable || atp->desc.ref_speed)
+ printf(" Reference speed: %d\n", clv_to_speed[atp->desc.ref_speed]);
+ IS("unrestricted", atp->desc.uru);
+/* printf(" Disk application code: %d\n", atp->desc.res5_05);*/
+ IS("erasable", atp->desc.erasable);
+ if (atp->desc.erasable)
+ sub_type = cdrw_subtypes[atp->desc.sub_type];
+ else
+ sub_type = cdr_subtypes[atp->desc.sub_type];
+ if (atp->desc.sub_type)
+ printf(" Disk sub type: %s (%d)\n", sub_type, atp->desc.sub_type);
+ printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(atp->desc.lead_in[1],
+ atp->desc.lead_in[2],
+ atp->desc.lead_in[3], FALSE),
+ atp->desc.lead_in[1],
+ atp->desc.lead_in[2],
+ atp->desc.lead_in[3]);
+ printf(" ATIP start of lead out: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(atp->desc.lead_out[1],
+ atp->desc.lead_out[2],
+ atp->desc.lead_out[3], TRUE),
+ atp->desc.lead_out[1],
+ atp->desc.lead_out[2],
+ atp->desc.lead_out[3]);
+ if (atp->desc.a1_v) {
+ if (atp->desc.erasable && atp->desc.sub_type == 1) {
+ speedvtab = hs_clv_to_speed;
+ }
+ if (atp->desc.a2_v && (atp->desc.sub_type == 2 || atp->desc.sub_type == 3)) {
+ speedvtab = us_clv_to_speed;
+ }
+ if (atp->desc.clv_low != 0 || atp->desc.clv_high != 0) {
+ atip_printspeed(" 1T speed low",
+ atp->desc.clv_low, speedvtab);
+ atip_printspeed(" 1T speed high",
+ atp->desc.clv_high, speedvtab);
+ printf("\n");
+ }
+ }
+ if (atp->desc.a2_v) {
+ Uint vlow;
+ Uint vhigh;
+
+ vlow = (atp->desc.a2[0] >> 4) & 0x07;
+ vhigh = atp->desc.a2[0] & 0x0F;
+
+ if (vlow != 0 || vhigh != 0) {
+ atip_printspeed(" 2T speed low",
+ vlow, speedvtab);
+ atip_printspeed(" 2T speed high",
+ vhigh, speedvtab);
+ printf("\n");
+ }
+ }
+ if (atp->desc.a1_v) {
+ printf(" power mult factor: %d %d\n", atp->desc.power_mult, atp->desc.tgt_y_pow);
+ if (atp->desc.erasable)
+ printf(" recommended erase/write power: %d\n", atp->desc.rerase_pwr_ratio);
+ }
+ if (atp->desc.a1_v) {
+ printf(" A1 values: %02X %02X %02X\n",
+ (&atp->desc.res15)[1],
+ (&atp->desc.res15)[2],
+ (&atp->desc.res15)[3]);
+ }
+ if (atp->desc.a2_v) {
+ printf(" A2 values: %02X %02X %02X\n",
+ atp->desc.a2[0],
+ atp->desc.a2[1],
+ atp->desc.a2[2]);
+ }
+ if (atp->desc.a3_v) {
+ printf(" A3 values: %02X %02X %02X\n",
+ atp->desc.a3[0],
+ atp->desc.a3[1],
+ atp->desc.a3[2]);
+ }
+}
+#endif /* PRINT_ATIP */
+
+static int
+speed_select_mmc(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ Uchar mode[0x100];
+ Uchar moder[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+ struct ricoh_mode_page_30 *rp = NULL;
+ int val;
+ BOOL forcespeed = FALSE;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp)
+ curspeed = *speedp;
+
+ /*
+ * Do not reset mp->test_write (-dummy) here.
+ */
+ deflt_writemodes_mmc(usalp, FALSE);
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+
+ if(dummy) {
+ mp->test_write = 1;
+ /* but it does not work on DVD+RW and -RAM, also bail out on other
+ * types that have not been tested yet */
+ int profile=get_curprofile(usalp);
+ switch(profile) {
+ case(0x12):
+ case(0x1a):
+ case(0x2a):
+ case(0x43):
+ case(0x52):
+ {
+ fprintf(stderr,
+ "Dummy mode not possible with %s.\n",
+ mmc_obtain_profile_name(profile) );
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ else
+ mp->test_write = 0;
+
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1))
+ return (-1);
+
+ /*
+ * Neither set nor get speed.
+ */
+ if (speedp == 0)
+ return (0);
+
+
+ rp = get_justlink_ricoh(usalp, moder);
+ if (mmc_isyamaha(usalp)) {
+ forcespeed = FALSE;
+ } else if (mmc_isplextor(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) {
+ int pwr;
+
+ pwr = check_powerrec_plextor(usalp);
+ if (pwr >= 0)
+ forcespeed = (pwr == 0);
+ } else if ((dp->cdr_flags & CDR_FORCESPEED) != 0) {
+ forcespeed = rp && rp->AWSCD != 0;
+ }
+
+ if (lverbose && (dp->cdr_flags & CDR_FORCESPEED) != 0)
+ printf("Forcespeed is %s.\n", forcespeed?"ON":"OFF");
+
+ if (!forcespeed && (dp->cdr_dstat->ds_cdrflags & RF_FORCESPEED) != 0) {
+ printf("Turning forcespeed on\n");
+ forcespeed = TRUE;
+ }
+ if (forcespeed && (dp->cdr_dstat->ds_cdrflags & RF_FORCESPEED) == 0) {
+ printf("Turning forcespeed off\n");
+ forcespeed = FALSE;
+ }
+ if (mmc_isplextor(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) {
+ powerrec_plextor(usalp, !forcespeed);
+ }
+ if (!mmc_isyamaha(usalp) && (dp->cdr_flags & CDR_FORCESPEED) != 0) {
+
+ if (rp) {
+ rp->AWSCD = forcespeed?1:0;
+ set_mode_params(usalp, "Ricoh Vendor Page", moder, moder[0]+1, 0, -1);
+ rp = get_justlink_ricoh(usalp, moder);
+ }
+ }
+
+ /*
+ * 44100 * 2 * 2 = 176400 bytes/s
+ *
+ * The right formula would be:
+ * tmp = (((long)curspeed) * 1764) / 10;
+ *
+ * But the standard is rounding the wrong way.
+ * Furtunately rounding down is guaranteed.
+ */
+ val = curspeed*177;
+ if (val > 0xFFFF)
+ val = 0xFFFF;
+ if (mmc_isyamaha(usalp) && forcespeed) {
+ if (force_speed_yamaha(usalp, -1, val) < 0)
+ return (-1);
+ } else if (mmc_set_speed(usalp, -1, val, ROTCTL_CLV) < 0) {
+ return (-1);
+ }
+
+ if (scsi_get_speed(usalp, 0, &val) >= 0) {
+ if (val > 0) {
+ fprintf(stderr, "Speed set to %d KB/s\n", val);
+ curspeed = val / 176;
+ *speedp = curspeed;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Some drives do not round up when writespeed is e.g. 1 and
+ * the minimum write speed of the drive is higher. Try to increment
+ * the write speed unti it gets accepted by the drive.
+ */
+static int
+mmc_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl)
+{
+ int rs;
+ int ws;
+ int ret = -1;
+ int c;
+ int k;
+
+ if (scsi_get_speed(usalp, &rs, &ws) >= 0) {
+ if (readspeed < 0)
+ readspeed = rs;
+ if (writespeed < 0)
+ writespeed = ws;
+ }
+ if (writespeed < 0 || writespeed > 0xFFFF)
+ return (ret);
+
+ usalp->silent++;
+ while (writespeed <= 0xFFFF) {
+ ret = scsi_set_speed(usalp, readspeed, writespeed, rotctl);
+ if (ret >= 0)
+ break;
+ c = usal_sense_code(usalp);
+ k = usal_sense_key(usalp);
+ /*
+ * Abort quickly if it does not make sense to repeat.
+ * 0x24 == Invalid field in cdb
+ * 0x24 means illegal speed.
+ */
+ if ((k != SC_ILLEGAL_REQUEST) || (c != 0x24)) {
+ if (usalp->silent <= 1)
+ usal_printerr(usalp);
+ usalp->silent--;
+ return (-1);
+ }
+ writespeed += 177;
+ }
+ if (ret < 0 && usalp->silent <= 1)
+ usal_printerr(usalp);
+ usalp->silent--;
+
+ return (ret);
+}
+
+static int
+speed_select_mdvd(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ int retcode;
+ char perf_desc[28];
+ int write_speed = *speedp * 1385;
+
+ /* For the moment we just divide the CD speed by 7*/
+
+ if(speedp!=NULL)
+ (*speedp)=(*speedp)*8;
+
+ memset(perf_desc, 0, sizeof(perf_desc));
+
+ /* Write Rotation Control = ROTCTL_CLV
+ * | Restore Logical Unit Defaults = 0
+ * | Exact = 0
+ * | Random Access = 0)
+ */
+ perf_desc[0]= ROTCTL_CLV << 3 | 0 << 2 | 0 << 1 | 0;
+ /* Start LBA to 0 */
+ perf_desc[4] = 0;
+ perf_desc[5] = 0;
+ perf_desc[6] = 0;
+ perf_desc[7] = 0;
+ /* End LBA set to 0 (setting to 0xffffffff failed on my LG burner
+ */
+ perf_desc[8] = 0;
+ perf_desc[9] = 0;
+ perf_desc[10] = 0;
+ perf_desc[11] = 0;
+ /* Read Speed = 0xFFFF */
+ perf_desc[12] = 0;
+ perf_desc[13] = 0;
+ perf_desc[14] = 0xFF;
+ perf_desc[15] = 0xFF;
+ /* Read Time = 1s */
+ perf_desc[18] = 1000 >> 8;
+ perf_desc[19] = 1000 & 0xFF;
+ /* Write Speed */
+ perf_desc[20] = write_speed >> 24;
+ perf_desc[21] = write_speed >> 16 & 0xFF;
+ perf_desc[22] = write_speed >> 8 & 0xFF;
+ perf_desc[23] = write_speed & 0xFF;
+ /* Write Time = 1s */
+ perf_desc[26] = 1000 >> 8;
+ perf_desc[27] = 1000 & 0xFF;
+
+ /* retcode = scsi_set_streaming(usalp, NULL, 0); */
+ retcode = scsi_set_streaming(usalp, perf_desc, sizeof(perf_desc));
+ if (retcode == -1) return retcode;
+ retcode = speed_select_mmc(usalp, dp, speedp);
+ if(speedp!=NULL)
+ (*speedp)=(*speedp)/7;
+ return retcode;
+}
+
+static int
+next_wr_addr_mmc(SCSI *usalp, track_t *trackp, long *ap)
+{
+ struct track_info track_info;
+ long next_addr;
+ int result = -1;
+
+
+ /*
+ * Reading info for current track may require doing the read_track_info
+ * with either the track number (if the track is currently being written)
+ * or with 0xFF (if the track hasn't been started yet and is invisible
+ */
+
+ if (trackp != 0 && trackp->track > 0 && is_packet(trackp)) {
+ usalp->silent++;
+ result = read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_TRACK,
+ trackp->trackno,
+ sizeof (track_info));
+ usalp->silent--;
+ }
+
+ if (result < 0) {
+ if (read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_TRACK, 0xFF,
+ sizeof (track_info)) < 0) {
+ errmsgno(EX_BAD, "Cannot get next writable address for 'invisible' track.\n");
+ errmsgno(EX_BAD, "This means that we are checking recorded media.\n");
+ errmsgno(EX_BAD, "This media cannot be written in streaming mode anymore.\n");
+ errmsgno(EX_BAD, "If you like to write to 'preformatted' RW media, try to blank the media first.\n");
+ return (-1);
+ }
+ }
+ if (usalp->verbose)
+ usal_prbytes("track info:", (Uchar *)&track_info,
+ sizeof (track_info)-usal_getresid(usalp));
+ next_addr = a_to_4_byte(track_info.next_writable_addr);
+ if (ap)
+ *ap = next_addr;
+ return (0);
+}
+
+static int
+write_leadin_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uint i;
+ long startsec = 0L;
+
+/* if (flags & F_SAO) {*/
+ if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) {
+ if (debug || lverbose) {
+ printf("Sending CUE sheet...\n");
+ flush();
+ }
+ if ((*dp->cdr_send_cue)(usalp, dp, trackp) < 0) {
+ errmsgno(EX_BAD, "Cannot send CUE sheet.\n");
+ return (-1);
+ }
+
+ (*dp->cdr_next_wr_address)(usalp, &trackp[0], &startsec);
+ if (trackp[0].flags & TI_TEXT) {
+ startsec = dp->cdr_dstat->ds_first_leadin;
+ printf("SAO startsec: %ld\n", startsec);
+ } else if (startsec <= 0 && startsec != -150) {
+ if(lverbose>2)
+ fprintf(stderr, "WARNING: Drive returns wrong startsec (%ld) using -150\n",
+ startsec);
+ startsec = -150;
+ }
+ if (debug)
+ printf("SAO startsec: %ld\n", startsec);
+
+ if (trackp[0].flags & TI_TEXT) {
+ if (startsec > 0) {
+ errmsgno(EX_BAD, "CD-Text must be in first session.\n");
+ return (-1);
+ }
+ if (debug || lverbose)
+ printf("Writing lead-in...\n");
+ if (write_cdtext(usalp, dp, startsec) < 0)
+ return (-1);
+
+ dp->cdr_dstat->ds_cdrflags |= RF_LEADIN;
+ } else for (i = 1; i <= trackp->tracks; i++) {
+ trackp[i].trackstart += startsec +150;
+ }
+#ifdef XXX
+ if (debug || lverbose)
+ printf("Writing lead-in...\n");
+
+ pad_track(usalp, dp, &trackp[1], -150, (Llong)0,
+ FALSE, 0);
+#endif
+ }
+/* if (flags & F_RAW) {*/
+ if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_RAW) {
+ /*
+ * In RAW write mode, we now write the lead in (TOC).
+ */
+ (*dp->cdr_next_wr_address)(usalp, &trackp[0], &startsec);
+ if (startsec > -4500) {
+ /*
+ * There must be at least 1 minute lead-in.
+ */
+ errmsgno(EX_BAD, "WARNING: Drive returns wrong startsec (%ld) using %ld from ATIP\n",
+ startsec, (long)dp->cdr_dstat->ds_first_leadin);
+ startsec = dp->cdr_dstat->ds_first_leadin;
+ }
+ if (startsec > -4500) {
+ errmsgno(EX_BAD, "Illegal startsec (%ld)\n", startsec);
+ return (-1);
+ }
+ if (debug || lverbose)
+ printf("Writing lead-in at sector %ld\n", startsec);
+ if (write_leadin(usalp, dp, trackp, startsec) < 0)
+ return (-1);
+ dp->cdr_dstat->ds_cdrflags |= RF_LEADIN;
+ }
+ return (0);
+}
+
+int st2mode[] = {
+ 0, /* 0 */
+ TM_DATA, /* 1 ST_ROM_MODE1 */
+ TM_DATA, /* 2 ST_ROM_MODE2 */
+ 0, /* 3 */
+ 0, /* 4 ST_AUDIO_NOPRE */
+ TM_PREEM, /* 5 ST_AUDIO_PRE */
+ 0, /* 6 */
+ 0, /* 7 */
+};
+
+static int
+next_wr_addr_mdvd(SCSI *usalp, track_t *trackp, long *ap)
+{
+ int track=0;
+ struct track_info track_info;
+ long next_addr;
+ int result = -1;
+ struct disk_info disk_info;
+ if (trackp){
+ track = trackp->trackno;
+ }
+
+ if (trackp != 0 && track > 0 && is_packet(trackp)) {
+ usalp->silent++;
+ result = read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_SESS, track, sizeof(track_info));
+ usalp->silent--;
+ if (scsi_in_progress(usalp)){
+ return -1;
+ }
+
+ }
+
+ if (result < 0) {
+ /* Get the last rzone*/
+ if(read_disk_info(usalp,(caddr_t)&disk_info,8)<0)
+ return (-1);
+
+ /* if (read_track_info(usalp, (caddr_t)&track_info, TI_TYPE_SESS, 0xFF, sizeof(track_info)) < 0) */
+ if (read_rzone_info(usalp, (caddr_t)&track_info, sizeof(track_info)) < 0)
+ return (-1);
+ }
+ if (usalp->verbose)
+ usal_prbytes("track info:", (Uchar *)&track_info,
+ sizeof(track_info)-usal_getresid(usalp));
+ next_addr = a_to_4_byte(track_info.next_writable_addr);
+ if (ap)
+ *ap = next_addr;
+ return (0);
+}
+
+static int
+open_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ if (trackp->pregapsize > 0 && (trackp->flags & TI_PREGAP) == 0) {
+ if (lverbose) {
+ printf("Writing pregap for track %d at %ld\n",
+ (int)trackp->trackno,
+ trackp->trackstart-trackp->pregapsize);
+ }
+ /*
+ * XXX Do we need to check isecsize too?
+ */
+ pad_track(usalp, dp, trackp,
+ trackp->trackstart-trackp->pregapsize,
+ (Llong)trackp->pregapsize*trackp->secsize,
+ FALSE, 0);
+ }
+ return (0);
+ }
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+
+/* mp->track_mode = ???;*/
+ mp->track_mode = st2mode[trackp->sectype & ST_MASK];
+/* mp->copy = ???;*/
+ mp->dbtype = trackp->dbtype;
+
+/*i_to_short(mp->audio_pause_len, 300);*/
+/*i_to_short(mp->audio_pause_len, 150);*/
+/*i_to_short(mp->audio_pause_len, 0);*/
+
+ if (is_packet(trackp)) {
+ mp->write_type = WT_PACKET;
+ mp->track_mode |= TM_INCREMENTAL;
+ mp->fp = (trackp->pktsize > 0) ? 1 : 0;
+ i_to_4_byte(mp->packet_size, trackp->pktsize);
+ } else if (is_tao(trackp)) {
+ mp->write_type = WT_TAO;
+ mp->fp = 0;
+ i_to_4_byte(mp->packet_size, 0);
+ } else {
+ errmsgno(EX_BAD, "Unknown write mode.\n");
+ return (-1);
+ }
+ if (trackp->isrc) {
+ mp->ISRC[0] = 0x80; /* Set ISRC valid */
+ strncpy((char *)&mp->ISRC[1], trackp->isrc, 12);
+
+ } else {
+ fillbytes(&mp->ISRC[0], sizeof (mp->ISRC), '\0');
+ }
+
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, trackp->secsize))
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ if (is_packet(trackp)) {
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "DVD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof(struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ mp->write_type = WT_PACKET;
+ mp->LS_V = 1;
+ /*For now we set the link size to 0x10(32k) because Pioneer-A03 only support this */
+ mp->link_size=0x10;
+ mp->fp = 1;
+ i_to_4_byte(mp->packet_size, trackp->pktsize);
+ } else {
+ return 0;
+ }
+
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, trackp->secsize))
+ return (-1);
+
+ return (0);
+}
+
+static int
+close_track_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int ret;
+
+ if (!is_tao(trackp) && !is_packet(trackp))
+ return (0);
+
+ if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) {
+ printf("Trouble flushing the cache\n");
+ return (-1);
+ }
+ wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */
+ if (is_packet(trackp) && !is_noclose(trackp)) {
+ /* close the incomplete track */
+ ret = scsi_close_tr_session(usalp, CL_TYPE_TRACK, 0xFF,
+ (dp->cdr_cmdflags&F_IMMED) != 0);
+ wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */
+ return (ret);
+ }
+ return (0);
+}
+
+static int
+close_track_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int ret;
+ if (!is_packet(trackp))
+ return (0);
+
+ if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) {
+ printf("Trouble flushing the cache\n");
+ return -1;
+ }
+ wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */
+ if (is_packet(trackp) && !is_noclose(trackp)) {
+ /* close the incomplete track */
+ ret = scsi_close_tr_session(usalp, 1, 0xFF, (dp->cdr_cmdflags&F_IMMED) != 0);
+ wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */
+ return (ret);
+ }
+ return (0);
+}
+
+int toc2sess[] = {
+ SES_DA_ROM, /* CD-DA */
+ SES_DA_ROM, /* CD-ROM */
+ SES_XA, /* CD-ROM XA mode 1 */
+ SES_XA, /* CD-ROM XA MODE 2 */
+ SES_CDI, /* CDI */
+ SES_DA_ROM, /* Invalid - use default */
+ SES_DA_ROM, /* Invalid - use default */
+};
+
+static int
+open_session_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uchar mode[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ mp->write_type = WT_TAO; /* fix to allow DAO later */
+ /*
+ * We need to set the right dbtype here because Sony drives
+ * don't like multi session in to be set with DB_ROM_MODE1
+ * which is set by us at the beginning as default as some drives
+ * have illegal default values.
+ */
+ mp->track_mode = st2mode[trackp[0].sectype & ST_MASK];
+ mp->dbtype = trackp[0].dbtype;
+
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ mp->write_type = WT_SAO;
+ if (dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER)
+ mp->write_type = 8;
+ mp->track_mode = 0;
+ mp->dbtype = DB_RAW;
+ }
+ if (is_raw(trackp)) {
+ mp->write_type = WT_RAW;
+ mp->track_mode = 0;
+
+ if (is_raw16(trackp)) {
+ mp->dbtype = DB_RAW_PQ;
+ } else if (is_raw96r(trackp)) {
+ mp->dbtype = DB_RAW_PW_R;
+ } else {
+ mp->dbtype = DB_RAW_PW;
+ }
+ }
+
+ mp->multi_session = (track_base(trackp)->tracktype & TOCF_MULTI) ?
+ MS_MULTI : MS_NONE;
+ mp->session_format = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+
+ if (trackp->isrc) {
+ mp->media_cat_number[0] = 0x80; /* Set MCN valid */
+ strncpy((char *)&mp->media_cat_number[1], trackp->isrc, 13);
+
+ } else {
+ fillbytes(&mp->media_cat_number[0], sizeof (mp->media_cat_number), '\0');
+ }
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1))
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_session_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uchar mode[0x100];
+ int tracks = trackp->tracks;
+
+ int len;
+ struct cd_mode_page_05 *mp;
+ Ulong totalsize;
+ int i;
+ int profile;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "DVD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof(struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+ if(is_packet(trackp)){
+ mp->write_type=WT_PACKET;
+ mp->fp=0;
+ mp->BUFE=1;
+ mp->track_mode=1;
+ }else{
+ mp->write_type = WT_SAO;
+ }
+
+ mp->multi_session = (track_base(trackp)->tracktype & TOCF_MULTI) ?
+ MS_MULTI : MS_NONE;
+ mp->session_format = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+
+ /* Enable Burnfree by default, allow to disable. XXX Sucks, duplicated functionality. */
+ if (dp->cdr_cdcap->BUF != 0) {
+ if (lverbose > 2)
+ fprintf(stderr,
+ "BURN-Free is %s.\n"
+ "Turning BURN-Free on\n",
+ mp->BUFE?"ON":"OFF");
+ mp->BUFE = 1;
+ }
+ if (driveropts != NULL) {
+ if ((strcmp(driveropts, "noburnproof") == 0 ||
+ strcmp(driveropts, "noburnfree") == 0)) {
+ if(lverbose>1)
+ fprintf(stderr, "Turning BURN-Free off\n");
+ mp->BUFE = 0;
+ }
+ else if ((strcmp(driveropts, "burnproof") == 0 ||
+ strcmp(driveropts, "burnfree") == 0)) {
+ /* a NOP, we enable burnfree by default */
+ if(lverbose>2)
+ fprintf(stderr, "Found burnproof/burnfree in driveropts, those options are enabled by default now.");
+ }
+ else if (strcmp(driveropts, "help") == 0) {
+ mmc_opthelp(dp, 0);
+ }
+ else {
+ errmsgno(EX_BAD, "Bad driver opts '%s'.\n", driveropts);
+ mmc_opthelp(dp, EX_BAD);
+ }
+ }
+
+
+ if (!set_mode_params(usalp, "DVD write parameter", mode, len, 0, -1))
+ return (-1);
+
+
+ totalsize=0;
+ for(i=1;i<=tracks;i++) {
+ totalsize+=trackp[i].tracksecs;
+ }
+
+ profile = get_curprofile(usalp);
+ if(!is_packet(trackp) && profile != 0x1A){
+ /* in DAO mode we need to reserve space for the track*/
+ if(reserve_track(usalp, totalsize)<0)
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+waitfix_mmc(SCSI *usalp, int secs)
+{
+ char dibuf[16];
+ int i;
+ int key;
+#define W_SLEEP 2
+
+ usalp->silent++;
+ for (i = 0; i < secs/W_SLEEP; i++) {
+ if (read_disk_info(usalp, dibuf, sizeof (dibuf)) >= 0) {
+ usalp->silent--;
+ return (0);
+ }
+ key = usal_sense_key(usalp);
+ if (key != SC_UNIT_ATTENTION && key != SC_NOT_READY)
+ break;
+ sleep(W_SLEEP);
+ }
+ usalp->silent--;
+ return (-1);
+#undef W_SLEEP
+}
+
+static int
+fixate_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int ret = 0;
+ int key = 0;
+ int code = 0;
+ struct timeval starttime;
+ struct timeval stoptime;
+ int dummy = (track_base(trackp)->tracktype & TOCF_DUMMY) != 0;
+
+ if(debug)
+ printf("fixate_mmc\n");
+ starttime.tv_sec = 0;
+ starttime.tv_usec = 0;
+ stoptime = starttime;
+ gettimeofday(&starttime, (struct timezone *)0);
+
+ if (dummy && lverbose)
+ printf("WARNING: Some drives don't like fixation in dummy mode.\n");
+
+ usalp->silent++;
+ if(debug)
+ printf("is_tao: %d,is_packet: %d\n", is_tao(trackp), is_packet(trackp));
+ if (is_tao(trackp) || is_packet(trackp)) {
+ ret = scsi_close_tr_session(usalp, CL_TYPE_SESSION, 0,
+ (dp->cdr_cmdflags&F_IMMED) != 0);
+ } else {
+ if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) {
+ if (!scsi_in_progress(usalp))
+ printf("Trouble flushing the cache\n");
+ }
+ }
+ usalp->silent--;
+ key = usal_sense_key(usalp);
+ code = usal_sense_code(usalp);
+
+ usalp->silent++;
+ if (debug && !unit_ready(usalp)) {
+ fprintf(stderr, "Early return from fixating. Ret: %d Key: %d, Code: %d\n", ret, key, code);
+ }
+ usalp->silent--;
+
+ if (ret >= 0) {
+ wait_unit_ready(usalp, 420/curspeed); /* XXX Wait for ATAPI */
+ waitfix_mmc(usalp, 420/curspeed); /* XXX Wait for ATAPI */
+ return (ret);
+ }
+
+ if ((dummy != 0 && (key != SC_ILLEGAL_REQUEST)) ||
+ /*
+ * Try to suppress messages from drives that don't like fixation
+ * in -dummy mode.
+ */
+ ((dummy == 0) &&
+ (((key != SC_UNIT_ATTENTION) && (key != SC_NOT_READY)) ||
+ ((code != 0x2E) && (code != 0x04))))) {
+ /*
+ * UNIT ATTENTION/2E seems to be a magic for old Mitsumi ATAPI drives
+ * NOT READY/ code 4 qual 7 (logical unit not ready, operation in progress)
+ * seems to be a magic for newer Mitsumi ATAPI drives
+ * NOT READY/ code 4 qual 8 (logical unit not ready, long write in progress)
+ * seems to be a magic for SONY drives
+ * when returning early from fixating.
+ * Try to supress the error message in this case to make
+ * simple minded users less confused.
+ */
+ usal_printerr(usalp);
+ usal_printresult(usalp); /* XXX restore key/code in future */
+ }
+
+ if (debug && !unit_ready(usalp)) {
+ fprintf(stderr, "Early return from fixating. Ret: %d Key: %d, Code: %d\n", ret, key, code);
+ }
+
+ wait_unit_ready(usalp, 420); /* XXX Wait for ATAPI */
+ waitfix_mmc(usalp, 420/curspeed); /* XXX Wait for ATAPI */
+
+ if (!dummy &&
+ (ret >= 0 || (key == SC_UNIT_ATTENTION && code == 0x2E))) {
+ /*
+ * Some ATAPI drives (e.g. Mitsumi) imply the
+ * IMMED bit in the SCSI cdb. As there seems to be no
+ * way to properly check for the real end of the
+ * fixating process we wait for the expected time.
+ */
+ gettimeofday(&stoptime, (struct timezone *)0);
+ timevaldiff(&starttime, &stoptime);
+ if (stoptime.tv_sec < (220 / curspeed)) {
+ unsigned secs;
+
+ if (lverbose) {
+ printf("Actual fixating time: %ld seconds\n",
+ (long)stoptime.tv_sec);
+ }
+ secs = (280 / curspeed) - stoptime.tv_sec;
+ if (lverbose) {
+ printf("ATAPI early return: sleeping %d seconds.\n",
+ secs);
+ }
+ sleep(secs);
+ }
+ }
+ return (ret);
+}
+
+static int
+fixate_mdvd(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int ret;
+ if (scsi_flush_cache(usalp, (dp->cdr_cmdflags&F_IMMED) != 0) < 0) {
+ printf("Trouble flushing the cache\n");
+ return -1;
+ }
+ wait_unit_ready(usalp, 300); /* XXX Wait for ATAPI */
+ /*set a really BIG timeout and call fixate_mmc
+ The BIG timeout is needed in case there was a very short rzone to write at the
+ beginning of the disk, because lead-out needs to be at some distance.
+ */
+ if(debug)
+ printf("fixate_mdvd\n");
+ usal_settimeout(usalp, 1000);
+ if(is_packet(trackp) || dp->profile == 0x1B){
+ scsi_close_tr_session(usalp, CL_TYPE_SESSION, 0, FALSE);
+ }
+ ret = fixate_mmc(usalp, dp, trackp);
+ if (dp->profile == 0x2B) {
+ scsi_close_tr_session(usalp, CL_TYPE_OPEN_SESSION, 0, FALSE);
+ scsi_close_tr_session(usalp, CL_TYPE_FINALISE_MINRAD, 0, FALSE);
+ }
+ usal_settimeout(usalp, 200);
+
+ return ret;
+}
+
+char *blank_types[] = {
+ "entire disk",
+ "PMA, TOC, pregap",
+ "incomplete track",
+ "reserved track",
+ "tail of track",
+ "closing of last session",
+ "last session",
+ "reserved blanking type",
+};
+
+char *format_types[] = {
+ "full format",
+ "background format",
+ "forced format",
+};
+
+static int
+blank_mmc(SCSI *usalp, cdr_t *dp, long addr, int blanktype)
+{
+ BOOL cdrr = FALSE; /* Read CD-R */
+ BOOL cdwr = FALSE; /* Write CD-R */
+ BOOL cdrrw = FALSE; /* Read CD-RW */
+ BOOL cdwrw = FALSE; /* Write CD-RW */
+ int ret;
+
+ mmc_check(usalp, &cdrr, &cdwr, &cdrrw, &cdwrw, NULL, NULL);
+ if (!cdwrw)
+ return (blank_dummy(usalp, dp, addr, blanktype));
+
+ if (dp->profile == 0x1A) {
+ printf("Error: this media does not support blanking, ignoring.\n");
+ return (blank_dummy(usalp, dp, addr, blanktype));
+ }
+ if (lverbose) {
+ printf("Blanking %s\n", blank_types[blanktype & 0x07]);
+ flush();
+ }
+
+ ret = scsi_blank(usalp, addr, blanktype, (dp->cdr_cmdflags&F_IMMED) != 0);
+ if (ret < 0)
+ return (ret);
+
+ wait_unit_ready(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */
+ waitfix_mmc(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */
+ return (ret);
+}
+
+static int format_mdvd(SCSI *usalp, cdr_t *dp, int formattype)
+{
+extern char *buf;
+ BOOL dvdwr = FALSE; /* Write DVD */
+ int ret;
+ int profile;
+ char addr[12];
+ struct disk_info *dip;
+
+ if (debug || lverbose > 2)
+ printf("format_mdvd\n");
+ mmc_check(usalp, NULL, NULL, NULL, NULL, NULL, &dvdwr);
+ if (!dvdwr)
+ return (format_dummy(usalp, dp, formattype));
+
+ if (debug || lverbose > 2)
+ printf("format_mdvd: drive is a dvd burner.\n");
+ profile = get_curprofile(usalp);
+ if (profile != 0x1A) {
+ printf("Error: only support DVD+RW formating, ignoring.\n");
+ return (format_dummy(usalp, dp, formattype));
+ }
+ dip = (struct disk_info *)buf;
+ if (get_diskinfo(usalp, dip) < 0)
+ return -1;
+
+ if (dip->disk_status & 3 && formattype != FORCE_FORMAT) {
+ printf("Error: disk already formated, ignoring.\n");
+ return -1;
+ }
+ addr[0] = 0; /* "Reserved" */
+ addr[1] = 2; /* "IMMED" flag */
+ addr[2] = 0; /* "Descriptor Length" (MSB) */
+ addr[3] = 8; /* "Descriptor Length" (LSB) */
+ addr[4+0] = 0xff;
+ addr[4+1] = 0xff;
+ addr[4+2] = 0xff;
+ addr[4+3] = 0xff;
+ addr[4+4] = 0x26<<2;
+ addr[4+5] = 0;
+ addr[4+6] = 0;
+ addr[4+7] = 0;
+ if (formattype == FORCE_FORMAT) {
+ printf("format_mdvd: forcing reformat.\n");
+ formattype = FULL_FORMAT;
+ addr[4+0] = 0;
+ addr[4+1] = 0;
+ addr[4+2] = 0;
+ addr[4+3] = 0;
+ addr[4+7] = 1;
+ } else {
+ printf("format_mdvd: media is unformated.\n");
+ }
+
+ if (lverbose) {
+ printf("Formating %s\n", format_types[formattype & 0x07]);
+ flush();
+ }
+ if (formattype == FULL_FORMAT) {
+ ret = scsi_format(usalp, (caddr_t)&addr, sizeof(addr), FALSE);
+ } else {
+ ret = scsi_format(usalp, (caddr_t)&addr, sizeof(addr), TRUE);
+ }
+ if (ret < 0)
+ return (ret);
+
+ wait_unit_ready(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */
+ waitfix_mmc(usalp, 90*60/curspeed); /* XXX Wait for ATAPI */
+ return (ret);
+}
+
+static int
+send_opc_mmc(SCSI *usalp, caddr_t bp, int cnt, int doopc)
+{
+ int ret;
+
+ usalp->silent++;
+ ret = send_opc(usalp, bp, cnt, doopc);
+ usalp->silent--;
+
+ if (ret >= 0)
+ return (ret);
+
+ /* BEGIN CSTYLED */
+ /*
+ * Hack for a mysterioys drive ....
+ * Device type : Removable CD-ROM
+ * Version : 0
+ * Response Format: 1
+ * Vendor_info : 'RWD '
+ * Identifikation : 'RW2224 '
+ * Revision : '2.53'
+ * Device seems to be: Generic mmc CD-RW.
+ *
+ * Performing OPC...
+ * CDB: 54 01 00 00 00 00 00 00 00 00
+ * Sense Bytes: 70 00 06 00 00 00 00 0A 00 00 00 00 5A 03 00 00
+ * Sense Key: 0x6 Unit Attention, Segment 0
+ * Sense Code: 0x5A Qual 0x03 (operator selected write permit) Fru 0x0
+ * Sense flags: Blk 0 (not valid)
+ */
+ /* END CSTYLED */
+ if (usal_sense_key(usalp) == SC_UNIT_ATTENTION &&
+ usal_sense_code(usalp) == 0x5A &&
+ usal_sense_qual(usalp) == 0x03)
+ return (0);
+
+ /*
+ * Do not make the condition:
+ * "Power calibration area almost full" a fatal error.
+ * It just flags that we have a single and last chance to write now.
+ */
+ if ((usal_sense_key(usalp) == SC_RECOVERABLE_ERROR ||
+ usal_sense_key(usalp) == SC_MEDIUM_ERROR) &&
+ usal_sense_code(usalp) == 0x73 &&
+ usal_sense_qual(usalp) == 0x01)
+ return (0);
+
+ /*
+ * Send OPC is optional.
+ */
+ if (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST) {
+ if (usalp->silent <= 0)
+ usal_printerr(usalp);
+ return (ret);
+ }
+ return (0);
+}
+
+static int
+opt1_mmc(SCSI *usalp, cdr_t *dp)
+{
+ int oflags = dp->cdr_dstat->ds_cdrflags;
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER) != 0) {
+ printf("Turning Audio Master Q. R. on\n");
+ if (set_audiomaster_yamaha(usalp, dp, TRUE) < 0)
+ return (-1);
+ if (!debug && lverbose <= 1)
+ dp->cdr_dstat->ds_cdrflags &= ~RF_PRATIP;
+ if (getdisktype_mmc(usalp, dp) < 0) {
+ dp->cdr_dstat->ds_cdrflags = oflags;
+ return (-1);
+ }
+ dp->cdr_dstat->ds_cdrflags = oflags;
+ if (oflags & RF_PRATIP) {
+ msf_t msf;
+ lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf);
+ printf("New start of lead in: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_first_leadin,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf);
+ printf("New start of lead out: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_maxblocks,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ }
+ }
+ if (mmc_isplextor(usalp)) {
+ int gcode;
+
+ if ((dp->cdr_flags & (CDR_SINGLESESS|CDR_HIDE_CDR)) != 0) {
+ if (ss_hide_plextor(usalp,
+ (dp->cdr_dstat->ds_cdrflags & RF_SINGLESESS) != 0,
+ (dp->cdr_dstat->ds_cdrflags & RF_HIDE_CDR) != 0) < 0)
+ return (-1);
+ }
+
+ if ((dp->cdr_flags & CDR_SPEEDREAD) != 0) {
+ if (speed_rd_plextor(usalp,
+ (dp->cdr_dstat->ds_cdrflags & RF_SPEEDREAD) != 0) < 0)
+ return (-1);
+ }
+
+ if ((dp->cdr_cmdflags & F_SETDROPTS) ||
+ (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) ||
+ (wm_base(dp->cdr_dstat->ds_wrmode) == WM_RAW))
+ gcode = do_gigarec_plextor(usalp);
+ else
+ gcode = gigarec_plextor(usalp, 0);
+ if (gcode != 0) {
+ msf_t msf;
+
+ dp->cdr_dstat->ds_first_leadin =
+ gigarec_mult(gcode, dp->cdr_dstat->ds_first_leadin);
+ dp->cdr_dstat->ds_maxblocks =
+ gigarec_mult(gcode, dp->cdr_dstat->ds_maxblocks);
+
+ if (oflags & RF_PRATIP) {
+ lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf);
+ printf("New start of lead in: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_first_leadin,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf);
+ printf("New start of lead out: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_maxblocks,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ }
+ }
+ }
+ return (0);
+}
+
+static int
+opt2_mmc(SCSI *usalp, cdr_t *dp)
+{
+ Uchar mode[0x100];
+ Uchar moder[0x100];
+ int len;
+ struct cd_mode_page_05 *mp;
+ struct ricoh_mode_page_30 *rp = NULL;
+ BOOL burnfree = FALSE;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len))
+ return (-1);
+ if (len == 0)
+ return (-1);
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+
+ rp = get_justlink_ricoh(usalp, moder);
+
+ if (dp->cdr_cdcap->BUF != 0) {
+ burnfree = (mp->BUFE != 0);
+ } else if ((dp->cdr_flags & CDR_BURNFREE) != 0) {
+ burnfree = (rp && (rp->BUEFE != 0));
+ }
+
+ if (lverbose>2 && (dp->cdr_flags & CDR_BURNFREE) != 0)
+ printf("BURN-Free is %s.\n", burnfree?"ON":"OFF");
+
+ if (!burnfree && (dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) != 0) {
+ if(lverbose>2)
+ printf("Turning BURN-Free on\n");
+ burnfree = TRUE;
+ }
+ if (burnfree && (dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) {
+ if(lverbose>2)
+ printf("Turning BURN-Free off\n");
+ burnfree = FALSE;
+ }
+ if (dp->cdr_cdcap->BUF != 0) {
+ mp->BUFE = burnfree?1:0;
+ }
+ else if ((dp->cdr_flags & CDR_BURNFREE) != 0) {
+
+ if (rp)
+ rp->BUEFE = burnfree?1:0;
+ }
+ if (rp) {
+ /*
+ * Clear Just-Link counter
+ */
+ i_to_2_byte(rp->link_counter, 0);
+ if (xdebug)
+ usal_prbytes("Mode Select Data ", moder, moder[0]+1);
+
+ if (!set_mode_params(usalp, "Ricoh Vendor Page", moder, moder[0]+1, 0, -1))
+ return (-1);
+ rp = get_justlink_ricoh(usalp, moder);
+ }
+
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1))
+ return (-1);
+
+ if (mmc_isplextor(usalp)) {
+ /*
+ * Clear Burn-Proof counter
+ */
+ usalp->silent++;
+ bpc_plextor(usalp, 1, NULL);
+ usalp->silent--;
+
+ do_varirec_plextor(usalp);
+ }
+
+ return (0);
+}
+
+static int
+opt1_mdvd(SCSI *usalp, cdr_t *dp)
+{
+ int oflags = dp->cdr_dstat->ds_cdrflags;
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_AUDIOMASTER) != 0) {
+ printf("Turning Audio Master Q. R. on\n");
+ if (set_audiomaster_yamaha(usalp, dp, TRUE) < 0)
+ return (-1);
+ if (!debug && lverbose <= 1)
+ dp->cdr_dstat->ds_cdrflags &= ~RF_PRATIP;
+ if (getdisktype_mdvd(usalp, dp) < 0) {
+ dp->cdr_dstat->ds_cdrflags = oflags;
+ return (-1);
+ }
+ dp->cdr_dstat->ds_cdrflags = oflags;
+ if (oflags & RF_PRATIP) {
+ msf_t msf;
+ lba_to_msf(dp->cdr_dstat->ds_first_leadin, &msf);
+ printf("New start of lead in: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_first_leadin,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ lba_to_msf(dp->cdr_dstat->ds_maxblocks, &msf);
+ printf("New start of lead out: %ld (%02d:%02d/%02d)\n",
+ (long)dp->cdr_dstat->ds_maxblocks,
+ msf.msf_min,
+ msf.msf_sec,
+ msf.msf_frame);
+ }
+ }
+ return (0);
+}
+
+static int
+scsi_sony_write(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ return (write_xg5(usalp, bp, sectaddr, size, blocks));
+}
+
+Uchar db2df[] = {
+ 0x00, /* 0 2352 bytes of raw data */
+ 0xFF, /* 1 2368 bytes (raw data + P/Q Subchannel) */
+ 0xFF, /* 2 2448 bytes (raw data + P-W Subchannel) */
+ 0xFF, /* 3 2448 bytes (raw data + P-W raw Subchannel)*/
+ 0xFF, /* 4 - Reserved */
+ 0xFF, /* 5 - Reserved */
+ 0xFF, /* 6 - Reserved */
+ 0xFF, /* 7 - Vendor specific */
+ 0x10, /* 8 2048 bytes Mode 1 (ISO/IEC 10149) */
+ 0x30, /* 9 2336 bytes Mode 2 (ISO/IEC 10149) */
+ 0xFF, /* 10 2048 bytes Mode 2! (CD-ROM XA form 1) */
+ 0xFF, /* 11 2056 bytes Mode 2 (CD-ROM XA form 1) */
+ 0xFF, /* 12 2324 bytes Mode 2 (CD-ROM XA form 2) */
+ 0xFF, /* 13 2332 bytes Mode 2 (CD-ROM XA 1/2+subhdr) */
+ 0xFF, /* 14 - Reserved */
+ 0xFF, /* 15 - Vendor specific */
+};
+
+static int
+gen_cue_mmc(track_t *trackp, void *vcuep, BOOL needgap)
+{
+ int tracks = trackp->tracks;
+ int i;
+ struct mmc_cue **cuep = vcuep;
+ struct mmc_cue *cue;
+ struct mmc_cue *cp;
+ int ncue = 0;
+ int icue = 0;
+ int pgsize;
+ msf_t m;
+ int ctl;
+ int df;
+ int scms;
+
+ cue = malloc(1);
+
+ for (i = 0; i <= tracks; i++) {
+ ctl = (st2mode[trackp[i].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[i]))
+ ctl |= TM_ALLOW_COPY << 4;
+ if (is_quadro(&trackp[i]))
+ ctl |= TM_QUADRO << 4;
+ df = db2df[trackp[i].dbtype & 0x0F];
+ if (trackp[i].tracktype == TOC_XA2 &&
+ trackp[i].sectype == (SECT_MODE_2_MIX|ST_MODE_RAW)) {
+ /*
+ * Hack for CUE with MODE2/CDI and
+ * trackp[i].dbtype == DB_RAW
+ */
+ df = 0x21;
+ }
+
+ if (trackp[i].isrc) { /* MCN or ISRC */
+ ncue += 2;
+ cue = realloc(cue, ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ if (i == 0) {
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[0], &cp->cs_tno, 7);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[7], &cp->cs_tno, 7);
+ } else {
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[0], &cp->cs_index, 6);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[6], &cp->cs_index, 6);
+ }
+ }
+ if (i == 0) { /* Lead in */
+ df &= ~7; /* Mask off data size & nonRAW subch */
+ if (df < 0x10)
+ df |= 1;
+ else
+ df |= 4;
+ if (trackp[0].flags & TI_TEXT) /* CD-Text in Lead-in*/
+ df |= 0x40;
+ lba_to_msf(-150, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, 0, &m);
+ } else {
+ scms = 0;
+
+ if (is_scms(&trackp[i]))
+ scms = 0x80;
+ pgsize = trackp[i].pregapsize;
+ if (pgsize == 0 && needgap)
+ pgsize++;
+ lba_to_msf(trackp[i].trackstart-pgsize, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, scms, &m);
+
+ if (trackp[i].nindex == 1) {
+ lba_to_msf(trackp[i].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 1, df, scms, &m);
+ } else {
+ int idx;
+ long *idxlist;
+
+ ncue += trackp[i].nindex;
+ idxlist = trackp[i].tindex;
+ cue = realloc(cue, ncue * sizeof (*cue));
+
+ for (idx = 1; idx <= trackp[i].nindex; idx++) {
+ lba_to_msf(trackp[i].trackstart + idxlist[idx], &m);
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, idx, df, scms, &m);
+ }
+ }
+ }
+ }
+ /* Lead out */
+ ctl = (st2mode[trackp[tracks+1].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[i]))
+ ctl |= TM_ALLOW_COPY << 4;
+ if (is_quadro(&trackp[i]))
+ ctl |= TM_QUADRO << 4;
+ df = db2df[trackp[tracks+1].dbtype & 0x0F];
+ if (trackp[i].tracktype == TOC_XA2 &&
+ trackp[i].sectype == (SECT_MODE_2_MIX|ST_MODE_RAW)) {
+ /*
+ * Hack for CUE with MODE2/CDI and
+ * trackp[i].dbtype == DB_RAW
+ */
+ df = 0x21;
+ }
+ df &= ~7; /* Mask off data size & nonRAW subch */
+ if (df < 0x10)
+ df |= 1;
+ else
+ df |= 4;
+ lba_to_msf(trackp[tracks+1].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, 0xAA, 1, df, 0, &m);
+
+ if (lverbose > 1) {
+ for (i = 0; i < ncue; i++) {
+ usal_prbytes("", (Uchar *)&cue[i], 8);
+ }
+ }
+ if (cuep)
+ *cuep = cue;
+ else
+ free(cue);
+ return (ncue);
+}
+
+static void
+fillcue(struct mmc_cue *cp /* The target cue entry */,
+ int ca /* Control/adr for this entry */,
+ int tno /* Track number for this entry */,
+ int idx /* Index for this entry */,
+ int dataform /* Data format for this entry */,
+ int scms /* Serial copy management */,
+ msf_t *mp /* MSF value for this entry */)
+{
+ cp->cs_ctladr = ca; /* XXX wie lead in */
+ cp->cs_tno = tno;
+ cp->cs_index = idx;
+ cp->cs_dataform = dataform; /* XXX wie lead in */
+ cp->cs_scms = scms;
+ cp->cs_min = mp->msf_min;
+ cp->cs_sec = mp->msf_sec;
+ cp->cs_frame = mp->msf_frame;
+}
+
+static int
+send_cue_mmc(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct mmc_cue *cp;
+ int ncue;
+ int ret;
+ Uint i;
+
+ for (i = 1; i <= trackp->tracks; i++) {
+ if (trackp[i].tracksize < (tsize_t)0) {
+ errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
+ return (-1);
+ }
+ }
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, FALSE);
+
+ usalp->silent++;
+ ret = send_cue_sheet(usalp, (caddr_t)cp, ncue*8);
+ usalp->silent--;
+ free(cp);
+ if (ret < 0) {
+ errmsgno(EX_BAD, "CUE sheet not accepted. Retrying with minimum pregapsize = 1.\n");
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, TRUE);
+ ret = send_cue_sheet(usalp, (caddr_t)cp, ncue*8);
+ if (ret < 0) {
+ errmsgno(EX_BAD,
+ "CUE sheet still not accepted. Please try to write in RAW (-raw96r) mode.\n");
+ }
+ free(cp);
+ }
+ return (ret);
+}
+
+static int
+stats_mmc(SCSI *usalp, cdr_t *dp)
+{
+ Uchar mode[256];
+ struct ricoh_mode_page_30 *rp;
+ UInt32_t count;
+
+ if (mmc_isplextor(usalp) && lverbose) {
+ int sels;
+ int maxs;
+ int lasts;
+
+ /*
+ * Run it in silent mode as old drives do not support it.
+ * As this function looks to be a part of the PowerRec
+ * features, we may want to check
+ * dp->cdr_flags & CDR_FORCESPEED
+ */
+ usalp->silent++;
+ if (get_speeds_plextor(usalp, &sels, &maxs, &lasts) >= 0) {
+ printf("Last selected write speed: %dx\n",
+ sels / 176);
+ printf("Max media write speed: %dx\n",
+ maxs / 176);
+ printf("Last actual write speed: %dx\n",
+ lasts / 176);
+ }
+ usalp->silent--;
+ }
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0)
+ return (0);
+
+ if (mmc_isplextor(usalp)) {
+ int i = 0;
+ int ret;
+
+ /*
+ * Read Burn-Proof counter
+ */
+ usalp->silent++;
+ ret = bpc_plextor(usalp, 2, &i);
+ usalp->silent--;
+ if (ret < 0)
+ return (-1);
+ count = i;
+ /*
+ * Clear Burn-Proof counter
+ */
+ bpc_plextor(usalp, 1, NULL);
+ } else {
+ rp = get_justlink_ricoh(usalp, mode);
+ if (rp)
+ count = a_to_u_2_byte(rp->link_counter);
+ else
+ return (-1);
+ }
+ if (lverbose) {
+ if (count == 0)
+ printf("BURN-Free was never needed.\n");
+ else
+ printf("BURN-Free was %d times used.\n",
+ (int)count);
+ }
+ return (0);
+}
+/*--------------------------------------------------------------------------*/
+static BOOL
+mmc_isplextor(SCSI *usalp)
+{
+ if (usalp->inq != NULL &&
+ strncmp(usalp->inq->vendor_info, "PLEXTOR", 7) == 0) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static BOOL
+mmc_isyamaha(SCSI *usalp)
+{
+ if (usalp->inq != NULL &&
+ strncmp(usalp->inq->vendor_info, "YAMAHA", 6) == 0) {
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+static void
+do_varirec_plextor(SCSI *usalp)
+{
+ char *p;
+ int voff;
+
+ p = hasdrvopt(driveropts, "varirec=");
+ if (p == NULL || curspeed != 4) {
+ if (check_varirec_plextor(usalp) >= 0)
+ varirec_plextor(usalp, FALSE, 0);
+ } else {
+ if (*astoi(p, &voff) != '\0')
+ comerrno(EX_BAD,
+ "Bad varirec value '%s'.\n", p);
+ if (check_varirec_plextor(usalp) < 0)
+ comerrno(EX_BAD, "Drive does not support VariRec.\n");
+ varirec_plextor(usalp, TRUE, voff);
+ }
+}
+
+/*
+ * GigaRec value table
+ */
+struct gr {
+ Uchar val;
+ char vadd;
+ char *name;
+} gr[] = {
+ { 0x00, 0, "off", },
+ { 0x00, 0, "1.0", },
+ { 0x01, 2, "1.2", },
+ { 0x02, 3, "1.3", },
+ { 0x03, 4, "1.4", },
+ { 0x81, -2, "0.8", },
+ { 0x82, -3, "0.7", },
+ { 0x83, -4, "0.6", },
+ { 0x00, 0, NULL, },
+};
+
+static int
+do_gigarec_plextor(SCSI *usalp)
+{
+ char *p;
+ int val = 0; /* Make silly GCC happy */
+
+ p = hasdrvopt(driveropts, "gigarec=");
+ if (p == NULL) {
+ if (check_gigarec_plextor(usalp) >= 0)
+ gigarec_plextor(usalp, 0);
+ } else {
+ struct gr *gp = gr;
+
+ for (; gp->name != NULL; gp++) {
+ if (streql(p, gp->name)) {
+ val = gp->val;
+ break;
+ }
+ }
+ if (gp->name == NULL)
+ comerrno(EX_BAD,
+ "Bad gigarec value '%s'.\n", p);
+ if (check_gigarec_plextor(usalp) < 0)
+ comerrno(EX_BAD, "Drive does not support GigaRec.\n");
+ return (gigarec_plextor(usalp, val));
+ }
+ return (0);
+}
+
+static int
+drivemode_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, void *modeval)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ if (modeval == NULL) {
+ scmd->flags |= SCG_RECV_DATA;
+ scmd->addr = bp;
+ scmd->size = cnt;
+ } else {
+ scmd->cdb.g5_cdb.res = 0x08;
+ }
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xE9;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = modecode;
+ if (modeval)
+ movebytes(modeval, &scmd->cdb.g1_cdb.addr[1], 6);
+ else
+ i_to_2_byte(&scmd->cdb.g1_cdb.count[2], cnt);
+
+ usalp->cmdname = "plextor drive mode";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * #defines for drivemode_plextor()...
+ */
+#define MODE_CODE_SH 0x01 /* Mode code for Single Session & Hide-CDR */
+#define MB1_SS 0x01 /* Single Session Mode */
+#define MB1_HIDE_CDR 0x02 /* Hide CDR Media */
+
+#define MODE_CODE_VREC 0x02 /* Mode code for Vari Rec */
+
+#define MODE_CODE_GREC 0x04 /* Mode code for Giga Rec */
+
+#define MODE_CODE_SPEED 0xbb /* Mode code for Speed Read */
+#define MBbb_SPEAD_READ 0x01 /* Spead Read */
+ /* Danach Speed auf 0xFFFF 0xFFFF setzen */
+
+static int
+drivemode2_plextor(SCSI *usalp, caddr_t bp, int cnt, int modecode, void *modeval)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ if (modeval == NULL) {
+ scmd->flags |= SCG_RECV_DATA;
+ scmd->addr = bp;
+ scmd->size = cnt;
+ } else {
+ scmd->cdb.g5_cdb.res = 0x08;
+ }
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xED;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = modecode;
+ if (modeval)
+ scmd->cdb.g5_cdb.reladr = *(char *)modeval != 0 ? 1 : 0;
+ else
+ i_to_2_byte(&scmd->cdb.g1_cdb.count[1], cnt);
+
+ usalp->cmdname = "plextor drive mode2";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+check_varirec_plextor(SCSI *usalp)
+{
+ int modecode = 2;
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ return (0);
+}
+
+static int
+check_gigarec_plextor(SCSI *usalp)
+{
+ int modecode = 4;
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ return (0);
+}
+
+static int
+varirec_plextor(SCSI *usalp, BOOL on, int val)
+{
+ int modecode = 2;
+ Uchar setmode[8];
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+
+ fillbytes(setmode, sizeof (setmode), '\0');
+ setmode[0] = on?1:0;
+ if (on) {
+ if (val < -2 || val > 2)
+ comerrno(EX_BAD, "Bad VariRec offset %d\n", val);
+ printf("Turning Varirec on.\n");
+ printf("Varirec offset is %d.\n", val);
+
+ if (val > 0) {
+ setmode[1] = val & 0x7F;
+ } else {
+ setmode[1] = (-val) & 0x7F;
+ setmode[1] |= 0x80;
+ }
+ }
+
+ if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0)
+ return (-1);
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0)
+ return (-1);
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+ return (0);
+}
+
+static int
+gigarec_plextor(SCSI *usalp, int val)
+{
+ int modecode = 4;
+ Uchar setmode[8];
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+
+ fillbytes(setmode, sizeof (setmode), '\0');
+ setmode[1] = val;
+
+ if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0)
+ return (-1);
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0)
+ return (-1);
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+ {
+ struct gr *gp = gr;
+
+ for (; gp->name != NULL; gp++) {
+ if (getmode[3] == gp->val)
+ break;
+ }
+ if (gp->name == NULL)
+ printf("Unknown GigaRec value 0x%X.\n", getmode[3]);
+ else
+ printf("GigaRec %sis %s.\n", gp->val?"value ":"", gp->name);
+ }
+ return (getmode[3]);
+}
+
+static Int32_t
+gigarec_mult(int code, Int32_t val)
+{
+ Int32_t add;
+ struct gr *gp = gr;
+
+ for (; gp->name != NULL; gp++) {
+ if (code == gp->val)
+ break;
+ }
+ if (gp->vadd == 0)
+ return (val);
+
+ add = val * gp->vadd / 10;
+ return (val + add);
+}
+
+static int
+check_ss_hide_plextor(SCSI *usalp)
+{
+ int modecode = 1;
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ return (getmode[2] & 0x03);
+}
+
+static int
+check_speed_rd_plextor(SCSI *usalp)
+{
+ int modecode = 0xBB;
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ return (getmode[2] & 0x01);
+}
+
+static int
+check_powerrec_plextor(SCSI *usalp)
+{
+ int modecode = 0;
+ Uchar getmode[8];
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (getmode[2] & 1)
+ return (1);
+
+ return (0);
+}
+
+static int
+ss_hide_plextor(SCSI *usalp, BOOL do_ss, BOOL do_hide)
+{
+ int modecode = 1;
+ Uchar setmode[8];
+ Uchar getmode[8];
+ BOOL is_ss;
+ BOOL is_hide;
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+
+ is_ss = (getmode[2] & MB1_SS) != 0;
+ is_hide = (getmode[2] & MB1_HIDE_CDR) != 0;
+
+ if (lverbose > 0) {
+ printf("Single session is %s.\n", is_ss ? "ON":"OFF");
+ printf("Hide CDR is %s.\n", is_hide ? "ON":"OFF");
+ }
+
+ fillbytes(setmode, sizeof (setmode), '\0');
+ setmode[0] = getmode[2]; /* Copy over old values */
+ if (do_ss >= 0) {
+ if (do_ss)
+ setmode[0] |= MB1_SS;
+ else
+ setmode[0] &= ~MB1_SS;
+ }
+ if (do_hide >= 0) {
+ if (do_hide)
+ setmode[0] |= MB1_HIDE_CDR;
+ else
+ setmode[0] &= ~MB1_HIDE_CDR;
+ }
+
+ if (do_ss >= 0 && do_ss != is_ss)
+ printf("Turning single session %s.\n", do_ss?"on":"off");
+ if (do_hide >= 0 && do_hide != is_hide)
+ printf("Turning hide CDR %s.\n", do_hide?"on":"off");
+
+ if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0)
+ return (-1);
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0)
+ return (-1);
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+ return (0);
+}
+
+static int
+speed_rd_plextor(SCSI *usalp, BOOL do_speedrd)
+{
+ int modecode = 0xBB;
+ Uchar setmode[8];
+ Uchar getmode[8];
+ BOOL is_speedrd;
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+
+ is_speedrd = (getmode[2] & MBbb_SPEAD_READ) != 0;
+
+ if (lverbose > 0)
+ printf("Speed-Read is %s.\n", is_speedrd ? "ON":"OFF");
+
+ fillbytes(setmode, sizeof (setmode), '\0');
+ setmode[0] = getmode[2]; /* Copy over old values */
+ if (do_speedrd >= 0) {
+ if (do_speedrd)
+ setmode[0] |= MBbb_SPEAD_READ;
+ else
+ setmode[0] &= ~MBbb_SPEAD_READ;
+ }
+
+ if (do_speedrd >= 0 && do_speedrd != is_speedrd)
+ printf("Turning Speed-Read %s.\n", do_speedrd?"on":"off");
+
+ if (drivemode_plextor(usalp, NULL, 0, modecode, setmode) < 0)
+ return (-1);
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ if (drivemode_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0)
+ return (-1);
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+ /*
+ * Set current read speed to new max value.
+ */
+ if (do_speedrd >= 0 && do_speedrd != is_speedrd)
+ scsi_set_speed(usalp, 0xFFFF, -1, ROTCTL_CAV);
+
+ return (0);
+}
+
+static int
+powerrec_plextor(SCSI *usalp, BOOL do_powerrec)
+{
+ int modecode = 0;
+ Uchar setmode[8];
+ Uchar getmode[8];
+ BOOL is_powerrec;
+ int speed;
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ usalp->silent++;
+ if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0) {
+ usalp->silent--;
+ return (-1);
+ }
+ usalp->silent--;
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+
+ is_powerrec = (getmode[2] & 1) != 0;
+
+ speed = a_to_u_2_byte(&getmode[4]);
+
+ if (lverbose > 0) {
+ printf("Power-Rec is %s.\n", is_powerrec ? "ON":"OFF");
+ printf("Power-Rec write speed: %dx (recommended)\n", speed / 176);
+ }
+
+ fillbytes(setmode, sizeof (setmode), '\0');
+ setmode[0] = getmode[2]; /* Copy over old values */
+ if (do_powerrec >= 0) {
+ if (do_powerrec)
+ setmode[0] |= 1;
+ else
+ setmode[0] &= ~1;
+ }
+
+ if (do_powerrec >= 0 && do_powerrec != is_powerrec)
+ printf("Turning Power-Rec %s.\n", do_powerrec?"on":"off");
+
+ if (drivemode2_plextor(usalp, NULL, 0, modecode, setmode) < 0)
+ return (-1);
+
+ fillbytes(getmode, sizeof (getmode), '\0');
+ if (drivemode2_plextor(usalp, (caddr_t)getmode, sizeof (getmode), modecode, NULL) < 0)
+ return (-1);
+
+ if (lverbose > 1)
+ usal_prbytes("Modes", getmode, sizeof (getmode));
+
+ return (0);
+}
+
+static int
+get_speeds_plextor(SCSI *usalp, int *selp, int *maxp, int *lastp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ char buf[10];
+ int i;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->flags |= SCG_RECV_DATA;
+ scmd->addr = buf;
+ scmd->size = sizeof (buf);
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xEB;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ i_to_2_byte(&scmd->cdb.g1_cdb.count[1], sizeof (buf));
+
+ usalp->cmdname = "plextor get speedlist";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ i = a_to_u_2_byte(&buf[4]);
+ if (selp)
+ *selp = i;
+
+ i = a_to_u_2_byte(&buf[6]);
+ if (maxp)
+ *maxp = i;
+
+ i = a_to_u_2_byte(&buf[8]);
+ if (lastp)
+ *lastp = i;
+
+ return (0);
+}
+
+static int
+bpc_plextor(SCSI *usalp, int mode, int *bpp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ char buf[4];
+ int i;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ fillbytes((caddr_t)buf, sizeof (buf), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->flags |= SCG_RECV_DATA;
+ scmd->addr = buf;
+ scmd->size = sizeof (buf);
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xF5;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ scmd->cdb.g5_cdb.addr[1] = 0x08;
+ scmd->cdb.g5_cdb.addr[2] = mode;
+
+ i_to_2_byte(&scmd->cdb.g1_cdb.count[1], sizeof (buf));
+
+ usalp->cmdname = "plextor read bpc";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usal_getresid(usalp) > 2)
+ return (0);
+
+ i = a_to_u_2_byte(buf);
+ if (bpp)
+ *bpp = i;
+
+ return (0);
+}
+
+static int
+set_audiomaster_yamaha(SCSI *usalp, cdr_t *dp, BOOL keep_mode)
+{
+ Uchar mode[0x100];
+ int len;
+ int ret = 0;
+ struct cd_mode_page_05 *mp;
+
+ if (xdebug && !keep_mode)
+ printf("Checking for Yamaha Audio Master feature: ");
+
+ /*
+ * Do not reset mp->test_write (-dummy) here.
+ */
+ deflt_writemodes_mmc(usalp, FALSE);
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x05, "CD write parameter",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ usalp->silent--;
+ return (-1);
+ }
+ if (len == 0) {
+ usalp->silent--;
+ return (-1);
+ }
+
+ mp = (struct cd_mode_page_05 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+#ifdef DEBUG
+ usal_prbytes("CD write parameter:", (Uchar *)mode, len);
+#endif
+
+ /*
+ * Do not set mp->test_write (-dummy) here. It should be set
+ * only at one place and only one time.
+ */
+ mp->BUFE = 0;
+
+ mp->write_type = 8;
+ mp->track_mode = 0;
+ mp->dbtype = DB_RAW;
+
+ if (!set_mode_params(usalp, "CD write parameter", mode, len, 0, -1))
+ ret = -1;
+
+ /*
+ * Do not reset mp->test_write (-dummy) here.
+ */
+ if (!keep_mode || ret < 0)
+ deflt_writemodes_mmc(usalp, FALSE);
+ usalp->silent--;
+
+ return (ret);
+}
+
+struct
+ricoh_mode_page_30 *get_justlink_ricoh(SCSI *usalp, Uchar *mode)
+{
+ Uchar modec[0x100];
+ int len;
+ struct ricoh_mode_page_30 *mp;
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x30, "Ricoh Vendor Page", mode, modec, NULL, NULL, &len)) {
+ usalp->silent--;
+ return ((struct ricoh_mode_page_30 *)0);
+ }
+ usalp->silent--;
+
+ /*
+ * SCSI mode header + 6 bytes mode page 30.
+ * This is including the Burn-Free counter.
+ */
+ if (len < 10)
+ return ((struct ricoh_mode_page_30 *)0);
+
+ if (xdebug) {
+ fprintf(stderr, "Mode len: %d\n", len);
+ usal_prbytes("Mode Sense Data ", mode, len);
+ usal_prbytes("Mode Sence CData", modec, len);
+ }
+
+ mp = (struct ricoh_mode_page_30 *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ /*
+ * 6 bytes mode page 30.
+ * This is including the Burn-Free counter.
+ */
+ if ((len - ((Uchar *)mp - mode) -1) < 5)
+ return ((struct ricoh_mode_page_30 *)0);
+
+ if (xdebug) {
+ fprintf(stderr, "Burnfree counter: %d\n", a_to_u_2_byte(mp->link_counter));
+ }
+ return (mp);
+}
+
+static int
+force_speed_yamaha(SCSI *usalp, int readspeed, int writespeed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xBB;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ if (readspeed < 0)
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], 0xFFFF);
+ else
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], readspeed);
+ if (writespeed < 0)
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xFFFF);
+ else
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], writespeed);
+
+ scmd->cdb.cmd_cdb[11] = 0x80;
+
+ usalp->cmdname = "yamaha force cd speed";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static BOOL
+get_tattoo_yamaha(SCSI *usalp, BOOL print, Int32_t *irp, Int32_t *orp)
+{
+ Uchar mode[0x100];
+ int len;
+ UInt32_t ival;
+ UInt32_t oval;
+ Uchar *mp;
+
+ usalp->silent++;
+ if (!get_mode_params(usalp, 0x31, "Yamaha Tattoo Page", mode, NULL, NULL, NULL, &len)) {
+ usalp->silent--;
+ return (FALSE);
+ }
+ usalp->silent--;
+
+ /*
+ * SCSI mode header + 16 bytes mode page 31.
+ * This is including the Burn-Free counter.
+ */
+ if (len < 20)
+ return (FALSE);
+
+ mp = (Uchar *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ /*
+ * 10 bytes mode page 31.
+ * This is including the Burn-Free counter.
+ */
+ if ((len - ((Uchar *)mp - mode) -1) < 10)
+ return (FALSE);
+
+ ival = a_to_u_3_byte(&mp[4]);
+ oval = a_to_u_3_byte(&mp[7]);
+
+ if (irp)
+ *irp = ival;
+ if (orp)
+ *orp = oval;
+
+ if (print && ival > 0 && oval > 0) {
+ printf("DiskT@2 inner r: %d\n", (int)ival);
+ printf("DiskT@2 outer r: %d\n", (int)oval);
+ printf("DiskT@2 image size: 3744 x %d pixel.\n",
+ (int)(oval-ival)+1);
+ }
+
+ return (TRUE);
+}
+
+static int
+do_tattoo_yamaha(SCSI *usalp, FILE *f)
+{
+ Int32_t ival = 0;
+ Int32_t oval = 0;
+ Int32_t lines;
+ off_t fsize;
+ char *buf = usalp->bufptr;
+ long bufsize = usalp->maxbuf;
+ long nsecs;
+ long amt;
+
+ nsecs = bufsize / 2048;
+ bufsize = nsecs * 2048;
+
+ if (!get_tattoo_yamaha(usalp, FALSE, &ival, &oval)) {
+ errmsgno(EX_BAD, "Cannot get DiskT@2 info.\n");
+ return (-1);
+ }
+
+ if (ival == 0 || oval == 0) {
+ errmsgno(EX_BAD, "DiskT@2 info not valid.\n");
+ return (-1);
+ }
+
+ lines = oval - ival + 1;
+ fsize = filesize(f);
+ if ((fsize % 3744) != 0 || fsize < (lines*3744)) {
+ errmsgno(EX_BAD, "Illegal DiskT@2 file size.\n");
+ return (-1);
+ }
+ if (fsize > (lines*3744))
+ fsize = lines*3744;
+
+ if (lverbose)
+ printf("Starting to write DiskT@2 data.\n");
+ fillbytes(buf, bufsize, '\0');
+ if ((amt = fileread(f, buf, bufsize)) <= 0) {
+ errmsg("DiskT@2 file read error.\n");
+ return (-1);
+ }
+
+ if (yamaha_write_buffer(usalp, 1, 0, ival, amt/2048, buf, amt) < 0) {
+ errmsgno(EX_BAD, "DiskT@2 1st write error.\n");
+ return (-1);
+ }
+ amt = (amt+2047) / 2048 * 2048;
+ fsize -= amt;
+
+ while (fsize > 0) {
+ fillbytes(buf, bufsize, '\0');
+ if ((amt = fileread(f, buf, bufsize)) <= 0) {
+ errmsg("DiskT@2 file read error.\n");
+ return (-1);
+ }
+ amt = (amt+2047) / 2048 * 2048;
+ fsize -= amt;
+ if (yamaha_write_buffer(usalp, 1, 0, 0, amt/2048, buf, amt) < 0) {
+ errmsgno(EX_BAD, "DiskT@2 write error.\n");
+ return (-1);
+ }
+ }
+
+ if (yamaha_write_buffer(usalp, 1, 0, oval, 0, buf, 0) < 0) {
+ errmsgno(EX_BAD, "DiskT@2 final error.\n");
+ return (-1);
+ }
+
+ wait_unit_ready(usalp, 1000); /* Wait for DiskT@2 */
+ waitfix_mmc(usalp, 1000); /* Wait for DiskT@2 */
+
+ return (0);
+}
+
+/*
+ * Yamaha specific version of 'write buffer' that offers an additional
+ * Parameter Length 'parlen' parameter.
+ */
+static int
+yamaha_write_buffer(SCSI *usalp, int mode, int bufferid, long offset,
+ long parlen, void *buffer, long buflen)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ Uchar *CDB;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = buffer;
+ scmd->size = buflen;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x3B;
+
+ CDB = (Uchar *)scmd->cdb.cmd_cdb;
+ CDB[1] = mode & 7;
+ CDB[2] = bufferid;
+ i_to_3_byte(&CDB[3], offset);
+ i_to_3_byte(&CDB[6], parlen);
+
+ usalp->cmdname = "write_buffer";
+
+ if (usal_cmd(usalp) >= 0)
+ return (1);
+ return (0);
+}
+
+static int
+dvd_dual_layer_split(SCSI *usalp, cdr_t *dp, long tsize)
+{
+ unsigned char xb[12];
+ long l0_size;
+
+ /* Get the Layer 0 defined data zone*/
+ if (read_dvd_structure(usalp, (caddr_t)xb, 12, 0, 0, 0x20) >= 0) {
+ if ((xb[1] | xb[0] << 8) < 13) {
+ fprintf(stderr, "dvd_dual_layer_split: read_dvd_structure returns invalid data\n");
+ return 1;
+ }
+ if (xb[4] & 0x80) {
+ printf("L0 zone size already set\n");
+ return 1;
+ }
+ l0_size = xb[11] | xb[10] << 8 | xb[9] << 16 | xb[8] << 24;
+ if (tsize < l0_size) {
+ fprintf(stderr, "track size smaller than one layer, use --force to force burning.");
+ return 0;
+ }
+ printf("L0 size: %ld (track size %ld)\n", l0_size, tsize);
+ l0_size = tsize / 2;
+ l0_size = l0_size - 1 + 16 - (l0_size - 1) % 16;
+ printf("New L0 size: %ld\n", l0_size);
+
+ memset (xb, 0, sizeof(xb));
+ xb[1] = sizeof(xb) - 2;
+ xb[8] = l0_size >> 24;
+ xb[9] = l0_size >> 16;
+ xb[10] = l0_size >> 8;
+ xb[11] = l0_size;
+ if (send_dvd_structure(usalp, (caddr_t)xb, 12, 0, 0x20)) {
+ fprintf(stderr, "dvd_dual_layer_split: send_dvd_structure failed, could not set middle zone location.\n");
+ return 0;
+ }
+ }
+ return 1;
+}
diff --git a/wodim/drv_philips.c b/wodim/drv_philips.c
new file mode 100644
index 0000000..13cf8ed
--- /dev/null
+++ b/wodim/drv_philips.c
@@ -0,0 +1,1346 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)drv_philips.c 1.69 05/05/16 Copyright 1997-2005 J. Schilling */
+/*
+ * CDR device implementation for
+ * Philips/Yamaha/Ricoh/Plasmon
+ *
+ * Copyright (c) 1997-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <unixstd.h> /* Include sys/types.h to make off_t available */
+#include <standard.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h> /* XXX Only for DEV_RICOH_RO_1060C */
+
+#include "wodim.h"
+
+extern int debug;
+extern int lverbose;
+
+static int load_unload_philips(SCSI *usalp, int);
+static int philips_load(SCSI *usalp, cdr_t *dp);
+static int philips_unload(SCSI *usalp, cdr_t *dp);
+static int philips_dumbload(SCSI *usalp, cdr_t *dp);
+static int philips_dumbunload(SCSI *usalp, cdr_t *dp);
+static int plasmon_buf(SCSI *, long *, long *);
+static int recover_philips(SCSI *usalp, cdr_t *dp, int);
+static int speed_select_yamaha(SCSI *usalp, cdr_t *dp, int *speedp);
+static int speed_select_philips(SCSI *usalp, cdr_t *dp, int *speedp);
+static int speed_select_oldphilips(SCSI *usalp, cdr_t *dp, int *speedp);
+static int speed_select_dumbphilips(SCSI *usalp, cdr_t *dp, int *speedp);
+static int speed_select_pioneer(SCSI *usalp, cdr_t *dp, int *speedp);
+static int philips_init(SCSI *usalp, cdr_t *dp);
+static int philips_getdisktype(SCSI *usalp, cdr_t *dp);
+static BOOL capacity_philips(SCSI *usalp, long *lp);
+static int first_writable_addr_philips(SCSI *usalp, long *, int, int, int,
+ int);
+static int next_wr_addr_philips(SCSI *usalp, track_t *trackp, long *ap);
+static int reserve_track_philips(SCSI *usalp, unsigned long);
+static int scsi_cdr_write_philips(SCSI *usalp, caddr_t bp, long sectaddr,
+ long size, int blocks, BOOL islast);
+static int write_track_info_philips(SCSI *usalp, int);
+static int write_track_philips(SCSI *usalp, long, int);
+static int open_track_philips(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_track_plasmon(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_track_oldphilips(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_track_yamaha(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int close_track_philips(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int fixation_philips(SCSI *usalp, cdr_t *dp, track_t *trackp);
+
+static int philips_attach(SCSI *usalp, cdr_t *);
+static int plasmon_attach(SCSI *usalp, cdr_t *);
+static int ricoh_attach(SCSI *usalp, cdr_t *);
+static int philips_getlilo(SCSI *usalp, long *lilenp, long *lolenp);
+
+
+struct cdd_52x_mode_page_21 { /* write track information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x0E = 14 Bytes */
+ Uchar res_2;
+ Uchar sectype;
+ Uchar track;
+ Uchar ISRC[9];
+ Uchar res[2];
+};
+
+struct cdd_52x_mode_page_23 { /* speed selection */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x06 = 6 Bytes */
+ Uchar speed;
+ Uchar dummy;
+ Uchar res[4];
+
+};
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct yamaha_mode_page_31 { /* drive configuration */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar res;
+ Ucbit dummy : 4;
+ Ucbit speed : 4;
+};
+
+#else /* Motorola byteorder */
+
+struct yamaha_mode_page_31 { /* drive configuration */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar res;
+ Ucbit speed : 4;
+ Ucbit dummy : 4;
+};
+#endif
+
+struct cdd_52x_mode_data {
+ struct scsi_mode_header header;
+ union cdd_pagex {
+ struct cdd_52x_mode_page_21 page21;
+ struct cdd_52x_mode_page_23 page23;
+ struct yamaha_mode_page_31 page31;
+ } pagex;
+};
+
+
+cdr_t cdr_philips_cdd521O = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "philips_cdd521_old",
+ "driver for Philips old CDD-521",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_oldphilips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_oldphilips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_philips_dumb = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "philips_dumb",
+ "driver for Philips CDD-521 with pessimistic assumptions",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_dumbload,
+ philips_dumbunload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_dumbphilips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_oldphilips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_philips_cdd521 = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "philips_cdd521",
+ "driver for Philips CDD-521",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_philips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_philips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_philips_cdd522 = {
+ 0, 0,
+/* CDR_TAO|CDR_SAO|CDR_TRAYLOAD,*/
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "philips_cdd522",
+ "driver for Philips CDD-522",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_philips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_philips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_tyuden_ew50 = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD|CDR_SWABAUDIO,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "tyuden_ew50",
+ "driver for Taiyo Yuden EW-50",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_philips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_philips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_kodak_pcd600 = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 6, 6,
+ "kodak_pcd_600",
+ "driver for Kodak PCD-600",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_philips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_oldphilips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_plasmon_rf4100 = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD,
+ CDR_CDRW_NONE,
+ 2, 4,
+ "plasmon_rf4100",
+ "driver for Plasmon RF 4100",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ plasmon_attach,
+ philips_init,
+ philips_getdisktype,
+ philips_load,
+ philips_unload,
+ plasmon_buf,
+ recovery_needed,
+ recover_philips,
+ speed_select_philips,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_plasmon,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_pioneer_dw_s114x = {
+ 0, 0,
+ CDR_TAO|CDR_TRAYLOAD|CDR_SWABAUDIO,
+ CDR_CDRW_NONE,
+ 2, 4,
+ "pioneer_dws114x",
+ "driver for Pioneer DW-S114X",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ philips_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_pioneer,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+/* open_track_yamaha,*/
+/*???*/ open_track_oldphilips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_yamaha_cdr100 = {
+ 0, 0,
+/* CDR_TAO|CDR_SAO|CDR_CADDYLOAD|CDR_SWABAUDIO,*/
+ CDR_TAO|CDR_CADDYLOAD|CDR_SWABAUDIO,
+ CDR_CDRW_NONE,
+ 2, 4,
+ "yamaha_cdr100",
+ "driver for Yamaha CDR-100 / CDR-102",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ philips_attach,
+ philips_init,
+ drive_getdisktype,
+ scsi_load,
+ philips_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_yamaha,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_yamaha,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_ricoh_ro1060 = {
+ 0, 0,
+/* CDR_TAO|CDR_SAO|CDR_CADDYLOAD,*/
+ CDR_TAO|CDR_CADDYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "ricoh_ro1060c",
+ "driver for Ricoh RO-1060C",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ ricoh_attach,
+ philips_init,
+ philips_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_yamaha,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_philips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_ricoh_ro1420 = {
+ 0, 0,
+/* CDR_TAO|CDR_SAO|CDR_CADDYLOAD,*/
+ CDR_TAO|CDR_CADDYLOAD,
+ CDR_CDRW_NONE,
+ 2, 2,
+ "ricoh_ro1420c",
+ "driver for Ricoh RO-1420C",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ ricoh_attach,
+ philips_init,
+ philips_getdisktype,
+ scsi_load,
+ scsi_unload,
+ buf_dummy,
+ recovery_needed,
+ recover_philips,
+ speed_select_yamaha,
+ select_secsize,
+ next_wr_addr_philips,
+ reserve_track_philips,
+ scsi_cdr_write_philips,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ no_sendcue,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_philips,
+ close_track_philips,
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset_philips,
+ fixation_philips,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+
+static int load_unload_philips(SCSI *usalp, int load)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE7;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.count[1] = !load;
+
+ usalp->cmdname = "philips medium load/unload";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+philips_load(SCSI *usalp, cdr_t *dp)
+{
+ return (load_unload_philips(usalp, 1));
+}
+
+static int
+philips_unload(SCSI *usalp, cdr_t *dp)
+{
+ return (load_unload_philips(usalp, 0));
+}
+
+static int
+philips_dumbload(SCSI *usalp, cdr_t *dp)
+{
+ int ret;
+
+ usalp->silent++;
+ ret = load_unload_philips(usalp, 1);
+ usalp->silent--;
+ if (ret < 0)
+ return (scsi_load(usalp, dp));
+ return (0);
+}
+
+static int
+philips_dumbunload(SCSI *usalp, cdr_t *dp)
+{
+ int ret;
+
+ usalp->silent++;
+ ret = load_unload_philips(usalp, 0);
+ usalp->silent--;
+ if (ret < 0)
+ return (scsi_unload(usalp, dp));
+ return (0);
+}
+
+static int
+plasmon_buf(SCSI *usalp,
+ long *sp /* Size pointer */,
+ long *fp /* Free space pointer */)
+{
+ /*
+ * There's no way to obtain these values from the
+ * Plasmon RF41xx devices. This function stub is only
+ * present to prevent cdrecord.c from calling the READ BUFFER
+ * SCSI cmd which is implemented non standard compliant in
+ * the Plasmon drive. Calling READ BUFFER would only jam the Plasmon
+ * as the non standard implementation in the Plasmon firmware
+ * expects different parameters.
+ */
+
+ if (sp)
+ *sp = 0L;
+ if (fp)
+ *fp = 0L;
+
+ return (100); /* 100 % */
+}
+
+static int
+recover_philips(SCSI *usalp, cdr_t *dp, int track)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xEC;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "philips recover";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+speed_select_yamaha(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int len = 16;
+ int page = 0x31;
+ struct yamaha_mode_page_31 *xp;
+ struct cdd_52x_mode_data md;
+ int count;
+ int speed = 1;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp) {
+ speed = *speedp;
+ } else {
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, page, "Speed/Dummy information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ return (-1);
+ }
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp = (struct yamaha_mode_page_31 *)mp;
+ speed = xp->speed;
+ }
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct yamaha_mode_page_31);
+
+ speed >>= 1;
+ md.pagex.page31.p_code = 0x31;
+ md.pagex.page31.p_len = 0x02;
+ md.pagex.page31.speed = speed;
+ md.pagex.page31.dummy = dummy?1:0;
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+speed_select_philips(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int len = 20;
+ int page = 0x23;
+ struct cdd_52x_mode_page_23 *xp;
+ struct cdd_52x_mode_data md;
+ int count;
+ int speed = 1;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp) {
+ speed = *speedp;
+ } else {
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, page, "Speed/Dummy information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ return (-1);
+ }
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp = (struct cdd_52x_mode_page_23 *)mp;
+ speed = xp->speed;
+ }
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct cdd_52x_mode_page_23);
+
+ md.pagex.page23.p_code = 0x23;
+ md.pagex.page23.p_len = 0x06;
+ md.pagex.page23.speed = speed;
+ md.pagex.page23.dummy = dummy?1:0;
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+speed_select_pioneer(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ if (speedp != 0 && *speedp < 2) {
+ *speedp = 2;
+ if (lverbose)
+ printf("WARNING: setting to minimum speed (2).\n");
+ }
+ return (speed_select_philips(usalp, dp, speedp));
+}
+
+static int
+speed_select_oldphilips(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (lverbose)
+ printf("WARNING: ignoring selected speed.\n");
+ if (dummy) {
+ errmsgno(EX_BAD, "Cannot set dummy writing for this device.\n");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+speed_select_dumbphilips(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ if (speed_select_philips(usalp, dp, speedp) < 0)
+ return (speed_select_oldphilips(usalp, dp, speedp));
+ return (0);
+}
+
+static int
+philips_init(SCSI *usalp, cdr_t *dp)
+{
+ return ((*dp->cdr_set_speed_dummy)(usalp, dp, NULL));
+}
+
+
+#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what);
+
+static int
+philips_getdisktype(SCSI *usalp, cdr_t *dp)
+{
+ dstat_t *dsp = dp->cdr_dstat;
+ char sbuf[16];
+ long dummy;
+ long lilen;
+ long lolen;
+ msf_t msf;
+ int audio = -1;
+
+ usalp->silent++;
+ dummy = (*dp->cdr_next_wr_address)(usalp, (track_t *)0, &lilen);
+ usalp->silent--;
+
+ /*
+ * Check for "Command sequence error" first.
+ */
+ if ((dsp->ds_cdrflags & RF_WRITE) != 0 &&
+ dummy < 0 &&
+ (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST ||
+ usal_sense_code(usalp) != 0x2C)) {
+ reload_media(usalp, dp);
+ }
+
+ usalp->silent++;
+ if (read_subchannel(usalp, sbuf, 0, 12, 0, 1, 0xf0) >= 0) {
+ if (sbuf[2] == 0 && sbuf[3] == 8)
+ audio = (sbuf[7] & 0x40) != 0;
+ }
+ usalp->silent--;
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 &&
+ dummy >= 0 && lilen == 0) {
+ usalp->silent++;
+ dummy = philips_getlilo(usalp, &lilen, &lolen);
+ usalp->silent--;
+
+ if (dummy >= 0) {
+/* printf("lead-in len: %d lead-out len: %d\n", lilen, lolen);*/
+ lba_to_msf(-150 - lilen, &msf);
+
+ printf("ATIP info from disk:\n");
+ if (audio >= 0)
+ IS("unrestricted", audio);
+ if (audio == 1 || (audio == 0 && (sbuf[7] & 0x3F) != 0x3F))
+ printf(" Disk application code: %d\n", sbuf[7] & 0x3F);
+ printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n",
+ -150 - lilen, msf.msf_min, msf.msf_sec, msf.msf_frame);
+
+ if (capacity_philips(usalp, &lolen)) {
+ lba_to_msf(lolen, &msf);
+ printf(
+ " ATIP start of lead out: %ld (%02d:%02d/%02d)\n",
+ lolen, msf.msf_min, msf.msf_sec, msf.msf_frame);
+ }
+ lba_to_msf(-150 - lilen, &msf);
+ pr_manufacturer(&msf,
+ FALSE, /* Always not erasable */
+ audio > 0); /* Audio from read subcode */
+ }
+ }
+
+ if (capacity_philips(usalp, &lolen)) {
+ dsp->ds_maxblocks = lolen;
+ dsp->ds_maxrblocks = disk_rcap(&msf, dsp->ds_maxblocks,
+ FALSE, /* Always not erasable */
+ audio > 0); /* Audio from read subcode */
+ }
+ usalp->silent++;
+ /*read_subchannel(usalp, bp, track, cnt, msf, subq, fmt); */
+
+ if (read_subchannel(usalp, sbuf, 0, 14, 0, 0, 0xf1) >= 0)
+ usal_prbytes("Disk bar code:", (Uchar *)sbuf, 14 - usal_getresid(usalp));
+ usalp->silent--;
+
+ return (drive_getdisktype(usalp, dp));
+}
+
+static BOOL
+capacity_philips(SCSI *usalp, long *lp)
+{
+ long l = 0L;
+ BOOL succeed = TRUE;
+
+ usalp->silent++;
+ if (read_B0(usalp, FALSE, NULL, &l) >= 0) {
+ if (debug)
+ printf("lead out B0: %ld\n", l);
+ *lp = l;
+ } else if (read_trackinfo(usalp, 0xAA, &l, NULL, NULL, NULL, NULL) >= 0) {
+ if (debug)
+ printf("lead out AA: %ld\n", l);
+ *lp = l;
+ } if (read_capacity(usalp) >= 0) {
+ l = usalp->cap->c_baddr + 1;
+ if (debug)
+ printf("lead out capacity: %ld\n", l);
+ } else {
+ succeed = FALSE;
+ }
+ *lp = l;
+ usalp->silent--;
+ return (succeed);
+}
+
+struct fwa {
+ char len;
+ char addr[4];
+ char res;
+};
+
+static int
+first_writable_addr_philips(SCSI *usalp, long *ap, int track, int isaudio,
+ int preemp, int npa)
+{
+ struct fwa fwa;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)&fwa, sizeof (fwa), '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)&fwa;
+ scmd->size = sizeof (fwa);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE2;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = track;
+ scmd->cdb.g1_cdb.addr[1] = isaudio ? (preemp ? 5 : 4) : 1;
+
+ scmd->cdb.g1_cdb.count[0] = npa?1:0;
+ scmd->cdb.g1_cdb.count[1] = sizeof (fwa);
+
+ usalp->cmdname = "first writeable address philips";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (ap)
+ *ap = a_to_4_byte(fwa.addr);
+ return (0);
+}
+
+static int
+next_wr_addr_philips(SCSI *usalp, track_t *trackp, long *ap)
+{
+
+/* if (first_writable_addr_philips(usalp, ap, 0, 0, 0, 1) < 0)*/
+ if (first_writable_addr_philips(usalp, ap, 0, 0, 0, 0) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+reserve_track_philips(SCSI *usalp, unsigned long len)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE4;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], len);
+
+ usalp->cmdname = "philips reserve_track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+scsi_cdr_write_philips(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ return (write_xg0(usalp, bp, 0, size, blocks));
+}
+
+static int
+write_track_info_philips(SCSI *usalp, int sectype)
+{
+ struct cdd_52x_mode_data md;
+ int count = sizeof (struct scsi_mode_header) +
+ sizeof (struct cdd_52x_mode_page_21);
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+ md.pagex.page21.p_code = 0x21;
+ md.pagex.page21.p_len = 0x0E;
+ /* is sectype ok ??? */
+ md.pagex.page21.sectype = sectype & ST_MASK;
+ md.pagex.page21.track = 0; /* 0 : create new track */
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+static int
+write_track_philips(SCSI *usalp,
+ long track /* track number 0 == new track */,
+ int sectype)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+/* scmd->flags = SCG_DISRE_ENA;*/
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE6;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, track);
+ scmd->cdb.g1_cdb.res6 = sectype & ST_MASK;
+
+ usalp->cmdname = "philips write_track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+open_track_philips(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (select_secsize(usalp, trackp->secsize) < 0)
+ return (-1);
+
+ if (write_track_info_philips(usalp, trackp->sectype) < 0)
+ return (-1);
+
+ if (write_track_philips(usalp, 0, trackp->sectype) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_track_plasmon(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (select_secsize(usalp, trackp->secsize) < 0)
+ return (-1);
+
+ if (write_track_info_philips(usalp, trackp->sectype) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_track_oldphilips(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (write_track_philips(usalp, 0, trackp->sectype) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_track_yamaha(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (select_secsize(usalp, trackp->secsize) < 0)
+ return (-1);
+
+ if (write_track_philips(usalp, 0, trackp->sectype) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+close_track_philips(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ return (scsi_flush_cache(usalp, FALSE));
+}
+
+static int fixation_philips(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+ scmd->cdb.g1_cdb.cmd = 0xE9;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.count[1] =
+ ((track_base(trackp)->tracktype & TOCF_MULTI) ? 8 : 0) |
+ (track_base(trackp)->tracktype & TOC_MASK);
+
+ usalp->cmdname = "philips fixation";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static const char *sd_cdd_521_error_str[] = {
+ "\003\000tray out", /* 0x03 */
+ "\062\000write data error with CU", /* 0x32 */ /* Yamaha */
+ "\063\000monitor atip error", /* 0x33 */
+ "\064\000absorbtion control error", /* 0x34 */
+#ifdef YAMAHA_CDR_100
+ /* Is this the same ??? */
+ "\120\000write operation in progress", /* 0x50 */
+#endif
+ "\127\000unable to read TOC/PMA/Subcode/ATIP", /* 0x57 */
+ "\132\000operator medium removal request", /* 0x5a */
+ "\145\000verify failed", /* 0x65 */
+ "\201\000illegal track number", /* 0x81 */
+ "\202\000command now not valid", /* 0x82 */
+ "\203\000medium removal is prevented", /* 0x83 */
+ "\204\000tray out", /* 0x84 */
+ "\205\000track at one not in PMA", /* 0x85 */
+ "\240\000stopped on non data block", /* 0xa0 */
+ "\241\000invalid start adress", /* 0xa1 */
+ "\242\000attampt to cross track-boundary", /* 0xa2 */
+ "\243\000illegal medium", /* 0xa3 */
+ "\244\000disk write protected", /* 0xa4 */
+ "\245\000application code conflict", /* 0xa5 */
+ "\246\000illegal blocksize for command", /* 0xa6 */
+ "\247\000blocksize conflict", /* 0xa7 */
+ "\250\000illegal transfer length", /* 0xa8 */
+ "\251\000request for fixation failed", /* 0xa9 */
+ "\252\000end of medium reached", /* 0xaa */
+#ifdef REAL_CDD_521
+ "\253\000non reserved reserved track", /* 0xab */
+#else
+ "\253\000illegal track number", /* 0xab */
+#endif
+ "\254\000data track length error", /* 0xac */
+ "\255\000buffer under run", /* 0xad */
+ "\256\000illegal track mode", /* 0xae */
+ "\257\000optical power calibration error", /* 0xaf */
+ "\260\000calibration area almost full", /* 0xb0 */
+ "\261\000current program area empty", /* 0xb1 */
+ "\262\000no efm at search address", /* 0xb2 */
+ "\263\000link area encountered", /* 0xb3 */
+ "\264\000calibration area full", /* 0xb4 */
+ "\265\000dummy data blocks added", /* 0xb5 */
+ "\266\000block size format conflict", /* 0xb6 */
+ "\267\000current command aborted", /* 0xb7 */
+ "\270\000program area not empty", /* 0xb8 */
+#ifdef YAMAHA_CDR_100
+ /* Used while writing lead in in DAO */
+ "\270\000write leadin in progress", /* 0xb8 */
+#endif
+ "\271\000parameter list too large", /* 0xb9 */
+ "\277\000buffer overflow", /* 0xbf */ /* Yamaha */
+ "\300\000no barcode available", /* 0xc0 */
+ "\301\000barcode reading error", /* 0xc1 */
+ "\320\000recovery needed", /* 0xd0 */
+ "\321\000cannot recover track", /* 0xd1 */
+ "\322\000cannot recover pma", /* 0xd2 */
+ "\323\000cannot recover leadin", /* 0xd3 */
+ "\324\000cannot recover leadout", /* 0xd4 */
+ "\325\000cannot recover opc", /* 0xd5 */
+ "\326\000eeprom failure", /* 0xd6 */
+ "\340\000laser current over", /* 0xe0 */ /* Yamaha */
+ "\341\000servo adjustment over", /* 0xe0 */ /* Yamaha */
+ NULL
+};
+
+static const char *sd_ro1420_error_str[] = {
+ "\004\000logical unit is in process of becoming ready", /* 04 00 */
+ "\011\200radial skating error", /* 09 80 */
+ "\011\201sledge servo failure", /* 09 81 */
+ "\011\202pll no lock", /* 09 82 */
+ "\011\203servo off track", /* 09 83 */
+ "\011\204atip sync error", /* 09 84 */
+ "\011\205atip/subcode jumped error", /* 09 85 */
+ "\127\300subcode not found", /* 57 C0 */
+ "\127\301atip not found", /* 57 C1 */
+ "\127\302no atip or subcode", /* 57 C2 */
+ "\127\303pma error", /* 57 C3 */
+ "\127\304toc read error", /* 57 C4 */
+ "\127\305disk informatoion error", /* 57 C5 */
+ "\144\200read in leadin", /* 64 80 */
+ "\144\201read in leadout", /* 64 81 */
+ "\201\000illegal track", /* 81 00 */
+ "\202\000command not now valid", /* 82 00 */
+ "\220\000reserve track check error", /* 90 00 */
+ "\220\001verify blank error", /* 90 01 */
+ "\221\001mode of last track error", /* 91 01 */
+ "\222\000header search error", /* 92 00 */
+ "\230\001header monitor error", /* 98 01 */
+ "\230\002edc error", /* 98 02 */
+ "\230\003read link, run-in run-out", /* 98 03 */
+ "\230\004last one block error", /* 98 04 */
+ "\230\005illegal blocksize", /* 98 05 */
+ "\230\006not all data transferred", /* 98 06 */
+ "\230\007cdbd over run error", /* 98 07 */
+ "\240\000stopped on non_data block", /* A0 00 */
+ "\241\000invalid start address", /* A1 00 */
+ "\243\000illegal medium", /* A3 00 */
+ "\246\000illegal blocksize for command", /* A6 00 */
+ "\251\000request for fixation failed", /* A9 00 */
+ "\252\000end of medium reached", /* AA 00 */
+ "\253\000illegal track number", /* AB 00 */
+ "\255\000buffer underrun", /* AD 00 */
+ "\256\000illegal track mode", /* AE 00 */
+ "\257\200power range error", /* AF 80 */
+ "\257\201moderation error", /* AF 81 */
+ "\257\202beta upper range error", /* AF 82 */
+ "\257\203beta lower range error", /* AF 83 */
+ "\257\204alpha upper range error", /* AF 84 */
+ "\257\205alpha lower range error", /* AF 85 */
+ "\257\206alpha and power range error", /* AF 86 */
+ "\260\000calibration area almost full", /* B0 00 */
+ "\261\000current program area empty", /* B1 00 */
+ "\262\000no efm at search address", /* B2 00 */
+ "\264\000calibration area full", /* B4 00 */
+ "\265\000dummy blocks added", /* B5 00 */
+ "\272\000write audio on reserved track", /* BA 00 */
+ "\302\200syscon rom error", /* C2 80 */
+ "\302\201syscon ram error", /* C2 81 */
+ "\302\220efm encoder error", /* C2 90 */
+ "\302\221efm decoder error", /* C2 91 */
+ "\302\222servo ic error", /* C2 92 */
+ "\302\223motor controller error", /* C2 93 */
+ "\302\224dac error", /* C2 94 */
+ "\302\225syscon eeprom error", /* C2 95 */
+ "\302\240block decoder communication error", /* C2 A0 */
+ "\302\241block encoder communication error", /* C2 A1 */
+ "\302\242block encoder/decoder path error", /* C2 A2 */
+ "\303\000CD-R engine selftest error", /* C3 xx */
+ "\304\000buffer parity error", /* C4 00 */
+ "\305\000data transfer error", /* C5 00 */
+ "\340\00012V failure", /* E0 00 */
+ "\341\000undefined syscon error", /* E1 00 */
+ "\341\001syscon communication error", /* E1 01 */
+ "\341\002unknown syscon error", /* E1 02 */
+ "\342\000syscon not ready", /* E2 00 */
+ "\343\000command rejected", /* E3 00 */
+ "\344\000command not accepted", /* E4 00 */
+ "\345\000verify error at beginning of track", /* E5 00 */
+ "\345\001verify error at ending of track", /* E5 01 */
+ "\345\002verify error at beginning of lead-in", /* E5 02 */
+ "\345\003verify error at ending of lead-in", /* E5 03 */
+ "\345\004verify error at beginning of lead-out", /* E5 04 */
+ "\345\005verify error at ending of lead-out", /* E5 05 */
+ "\377\000command phase timeout error", /* FF 00 */
+ "\377\001data in phase timeout error", /* FF 01 */
+ "\377\002data out phase timeout error", /* FF 02 */
+ "\377\003status phase timeout error", /* FF 03 */
+ "\377\004message in phase timeout error", /* FF 04 */
+ "\377\005message out phase timeout error", /* FF 05 */
+ NULL
+};
+
+static int
+philips_attach(SCSI *usalp, cdr_t *dp)
+{
+ usal_setnonstderrs(usalp, sd_cdd_521_error_str);
+ return (0);
+}
+
+static int
+plasmon_attach(SCSI *usalp, cdr_t *dp)
+{
+ usalp->inq->data_format = 1; /* Correct the ly */
+
+ usal_setnonstderrs(usalp, sd_cdd_521_error_str);
+ return (0);
+}
+
+static int
+ricoh_attach(SCSI *usalp, cdr_t *dp)
+{
+ if (dp == &cdr_ricoh_ro1060) {
+ errmsgno(EX_BAD, "No support for Ricoh RO-1060C\n");
+ return (-1);
+ }
+ usal_setnonstderrs(usalp, sd_ro1420_error_str);
+ return (0);
+}
+
+static int
+philips_getlilo(SCSI *usalp, long *lilenp, long *lolenp)
+{
+ char buf[4];
+ long li, lo;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = buf;
+ scmd->size = sizeof (buf);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xEE; /* Read session info */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, sizeof (buf));
+
+ usalp->cmdname = "philips read session info";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ if (usalp->verbose)
+ usal_prbytes("Session info data: ", (Uchar *)buf, sizeof (buf) - usal_getresid(usalp));
+
+ li = a_to_u_2_byte(buf);
+ lo = a_to_u_2_byte(&buf[2]);
+
+ if (lilenp)
+ *lilenp = li;
+ if (lolenp)
+ *lolenp = lo;
+
+ return (0);
+}
diff --git a/wodim/drv_simul.c b/wodim/drv_simul.c
new file mode 100644
index 0000000..f7f7e06
--- /dev/null
+++ b/wodim/drv_simul.c
@@ -0,0 +1,393 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)drv_simul.c 1.48 05/05/16 Copyright 1998-2005 J. Schilling */
+/*
+ * Simulation device driver
+ *
+ * Copyright (c) 1998-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+#include <utypes.h>
+#include <btorder.h>
+#include <schily.h>
+
+/*#include <usalio.h>*/
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include <libport.h>
+
+#include "wodim.h"
+
+extern int silent;
+extern int verbose;
+extern int lverbose;
+
+static int simul_load(SCSI *usalp, cdr_t *);
+static int simul_unload(SCSI *usalp, cdr_t *);
+static cdr_t *identify_simul(SCSI *usalp, cdr_t *, struct scsi_inquiry *);
+static int init_simul(SCSI *usalp, cdr_t *dp);
+static int getdisktype_simul(SCSI *usalp, cdr_t *dp);
+static int speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp);
+static int next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap);
+static int cdr_write_simul(SCSI *usalp, caddr_t bp, long sectaddr, long size,
+ int blocks, BOOL islast);
+static int open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static void tv_sub(struct timeval *tvp1, struct timeval *tvp2);
+
+static int simul_load(SCSI *usalp, cdr_t *dp)
+{
+ return (0);
+}
+
+static int simul_unload(SCSI *usalp, cdr_t *dp)
+{
+ return (0);
+}
+
+cdr_t cdr_cdr_simul = {
+ 0, 0,
+ CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_TRAYLOAD|CDR_SIMUL,
+ CDR_CDRW_ALL,
+ 40, 372,
+ "cdr_simul",
+ "simulation CD-R driver for timing/speed tests",
+ 0,
+ (dstat_t *)0,
+ identify_simul,
+ drive_attach,
+ init_simul,
+ getdisktype_simul,
+ simul_load,
+ simul_unload,
+ buf_dummy,
+ cmd_dummy, /* recovery_needed */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_simul,
+ select_secsize,
+ next_wr_addr_simul,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ cdr_write_simul,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_simul,
+ close_track_simul,
+ open_session_simul,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ fixate_simul,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+cdr_t cdr_dvd_simul = {
+ 0, 0,
+ CDR_TAO|CDR_SAO|CDR_PACKET|CDR_RAW|CDR_RAW16|CDR_RAW96P|CDR_RAW96R|CDR_SRAW96P|CDR_SRAW96R|CDR_DVD|CDR_TRAYLOAD|CDR_SIMUL,
+ CDR_CDRW_ALL,
+ 2, 1000,
+ "dvd_simul",
+ "simulation DVD-R driver for timing/speed tests",
+ 0,
+ (dstat_t *)0,
+ identify_simul,
+ drive_attach,
+ init_simul,
+ getdisktype_simul,
+ simul_load,
+ simul_unload,
+ buf_dummy,
+ cmd_dummy, /* recovery_needed */
+ (int(*)(SCSI *, cdr_t *, int))cmd_dummy, /* recover */
+ speed_select_simul,
+ select_secsize,
+ next_wr_addr_simul,
+ (int(*)(SCSI *, Ulong))cmd_ill, /* reserve_track */
+ cdr_write_simul,
+ (int(*)(track_t *, void *, BOOL))cmd_dummy, /* gen_cue */
+ (int(*)(SCSI *usalp, cdr_t *, track_t *))cmd_dummy, /* send_cue */
+ (int(*)(SCSI *, cdr_t *, track_t *))cmd_dummy, /* leadin */
+ open_track_simul,
+ close_track_simul,
+ open_session_simul,
+ cmd_dummy,
+ cmd_dummy, /* abort */
+ read_session_offset,
+ fixate_simul,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+static cdr_t *
+identify_simul(SCSI *usalp, cdr_t *dp, struct scsi_inquiry *ip)
+{
+ return (dp);
+}
+
+static long simul_nwa;
+static int simul_speed = 1;
+static int simul_dummy;
+static int simul_isdvd;
+static int simul_bufsize = 1024;
+static Uint sleep_rest;
+static Uint sleep_max;
+static Uint sleep_min;
+
+static int
+init_simul(SCSI *usalp, cdr_t *dp)
+{
+ return (speed_select_simul(usalp, dp, NULL));
+}
+
+static int
+getdisktype_simul(SCSI *usalp, cdr_t *dp)
+{
+ dstat_t *dsp = dp->cdr_dstat;
+
+ if (strcmp(dp->cdr_drname, cdr_cdr_simul.cdr_drname) == 0) {
+ dsp->ds_maxblocks = 333000;
+ simul_isdvd = FALSE;
+ } else {
+ dsp->ds_maxblocks = 2464153; /* 4.7 GB */
+/* dsp->ds_maxblocks = 1927896;*/ /* 3.95 GB */
+ dsp->ds_flags |= DSF_DVD;
+ simul_isdvd = TRUE;
+ }
+ return (drive_getdisktype(usalp, dp));
+}
+
+
+static int
+speed_select_simul(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ long val;
+ char *p;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp)
+ simul_speed = *speedp;
+ simul_dummy = dummy;
+
+ if ((p = getenv("CDR_SIMUL_BUFSIZE")) != NULL) {
+ if (getnum(p, &val) == 1)
+ simul_bufsize = val / 1024;
+ }
+
+ /*
+ * sleep_max is the time to empty the drive's buffer in µs.
+ * sector size is from 2048 bytes to 2352 bytes.
+ * If sector size is 2048 bytes, 1k takes 6.666 ms.
+ * If sector size is 2352 bytes, 1k takes 5.805 ms.
+ * We take the 6 ms as an average between both values.
+ * simul_bufsize is the number of kilobytes in drive buffer.
+ */
+ sleep_max = 6 * 1000 * simul_bufsize / simul_speed;
+
+ /*
+ * DVD single speed is 1385 * 1000 Bytes/s (676.27 sectors/s)
+ */
+ if ((dp->cdr_flags & CDR_DVD) != 0)
+ sleep_max = 739 * simul_bufsize / simul_speed;
+
+ if (lverbose) {
+ printf("Simulation drive buffer size: %d KB\n", simul_bufsize);
+ printf("Maximum reserve time in drive buffer: %d.%3.3d ms for speed %dx\n",
+ sleep_max / 1000,
+ sleep_max % 1000,
+ simul_speed);
+ }
+ return (0);
+}
+
+static int
+next_wr_addr_simul(SCSI *usalp, track_t *trackp, long *ap)
+{
+ /*
+ * This will most likely not 100% correct for TAO CDs
+ * but it is better than returning 0 in all cases.
+ */
+ if (ap)
+ *ap = simul_nwa;
+ return (0);
+}
+
+
+static int
+cdr_write_simul(SCSI *usalp, caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ Uint sleep_time;
+ Uint sleep_diff;
+
+ struct timeval tv1;
+ static struct timeval tv2;
+
+ if (lverbose > 1 && islast)
+ printf("\nWriting last record for this track.\n");
+
+ simul_nwa += blocks;
+
+ gettimeofday(&tv1, (struct timezone *)0);
+ if (tv2.tv_sec != 0) { /* Already did gettimeofday(&tv2) */
+ tv_sub(&tv1, &tv2);
+ if (sleep_rest != 0) {
+ sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
+
+ if (sleep_min > (sleep_rest - sleep_diff))
+ sleep_min = (sleep_rest - sleep_diff);
+
+ if (sleep_diff > sleep_rest) {
+ printf("Buffer underrun: actual delay was %d.%3.3d ms, max delay was %d.%3.3d ms.\n",
+ sleep_diff / 1000,
+ sleep_diff % 1000,
+ sleep_rest / 1000,
+ sleep_rest % 1000);
+ if (!simul_dummy)
+ return (-1);
+ }
+ /*
+ * If we spent time outside the write function
+ * subtract this time.
+ */
+ sleep_diff = tv1.tv_sec * 1000000 + tv1.tv_usec;
+ if (sleep_rest >= sleep_diff)
+ sleep_rest -= sleep_diff;
+ else
+ sleep_rest = 0;
+ }
+ }
+ /*
+ * Speed 1 ist 150 Sektoren/s
+ * Bei DVD 767.27 Sektoren/s
+ */
+ sleep_time = 1000000 * blocks / 75 / simul_speed;
+ if (simul_isdvd)
+ sleep_time = 1000000 * blocks / 676 / simul_speed;
+
+ sleep_time += sleep_rest;
+
+ if (sleep_time > sleep_max) {
+ int mod;
+ long rsleep;
+
+ sleep_rest = sleep_max;
+ sleep_time -= sleep_rest;
+ mod = sleep_time % 20000;
+ sleep_rest += mod;
+ sleep_time -= mod;
+ if (sleep_time > 0) {
+ gettimeofday(&tv1, (struct timezone *)0);
+ usleep(sleep_time);
+ gettimeofday(&tv2, (struct timezone *)0);
+ tv2.tv_sec -= tv1.tv_sec;
+ tv2.tv_usec -= tv1.tv_usec;
+ rsleep = tv2.tv_sec * 1000000 + tv2.tv_usec;
+ sleep_rest -= rsleep - sleep_time;
+ }
+ } else {
+ sleep_rest = sleep_time;
+ }
+
+ gettimeofday(&tv2, (struct timezone *)0);
+ return (size);
+}
+
+static int
+open_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ sleep_min = 999 * 1000000;
+ return (0);
+}
+
+static int
+close_track_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (lverbose) {
+ printf("Remaining reserve time in drive buffer: %d.%3.3d ms\n",
+ sleep_rest / 1000,
+ sleep_rest % 1000);
+ printf("Minimum reserve time in drive buffer: %d.%3.3d ms\n",
+ sleep_min / 1000,
+ sleep_min % 1000);
+ }
+ usleep(sleep_rest);
+ sleep_rest = 0;
+ return (0);
+}
+
+static int
+open_session_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ simul_nwa = 0L;
+ return (0);
+}
+
+static int
+fixate_simul(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ return (0);
+}
+
+static void
+tv_sub(struct timeval *tvp1, struct timeval *tvp2)
+{
+ tvp1->tv_sec -= tvp2->tv_sec;
+ tvp1->tv_usec -= tvp2->tv_usec;
+
+ while (tvp1->tv_usec < 0) {
+ tvp1->tv_usec += 1000000;
+ tvp1->tv_sec -= 1;
+ }
+}
diff --git a/wodim/drv_sony.c b/wodim/drv_sony.c
new file mode 100644
index 0000000..af8af68
--- /dev/null
+++ b/wodim/drv_sony.c
@@ -0,0 +1,1364 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)drv_sony.c 1.72 05/05/16 Copyright 1997-2005 J. Schilling */
+/*
+ * CDR device implementation for
+ * Sony
+ *
+ * Copyright (c) 1997-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*#define SONY_DEBUG*/
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* Include sys/types.h to make off_t available */
+#include <standard.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+
+#ifdef SONY_DEBUG
+# define inc_verbose() usalp->verbose++
+# define dec_verbose() usalp->verbose--
+#else
+# define inc_verbose()
+# define dec_verbose()
+#endif
+
+extern int debug;
+extern int lverbose;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+
+struct sony_924_mode_page_20 { /* mastering information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x06 = 6 Bytes */
+ Uchar subcode_header_off;
+ Ucbit res3_0 : 1;
+ Ucbit speudo : 1;
+ Ucbit res3_2 : 1;
+ Ucbit c2po : 1;
+ Ucbit subcode_ecc : 1;
+ Ucbit res3_567 : 3;
+ Uchar res_4;
+ Uchar cue_sheet_opt;
+ Uchar res[2];
+};
+
+#else /* Motorola byteorder */
+
+struct sony_924_mode_page_20 { /* mastering information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x06 = 6 Bytes */
+ Uchar subcode_header_off;
+ Ucbit res3_567 : 3;
+ Ucbit subcode_ecc : 1;
+ Ucbit c2po : 1;
+ Ucbit res3_2 : 1;
+ Ucbit speudo : 1;
+ Ucbit res3_0 : 1;
+ Uchar res_4;
+ Uchar cue_sheet_opt;
+ Uchar res[2];
+};
+#endif
+
+struct sony_924_mode_page_22 { /* disk information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x1E = 30 Bytes */
+ Uchar disk_style;
+ Uchar disk_type;
+ Uchar first_track;
+ Uchar last_track;
+ Uchar numsess;
+ Uchar res_7;
+ Uchar disk_appl_code[4];
+ Uchar last_start_time[4];
+ Uchar disk_status;
+ Uchar num_valid_nra;
+ Uchar track_info_track;
+ Uchar post_gap;
+ Uchar disk_id_code[4];
+ Uchar lead_in_start[4];
+ Uchar res[4];
+};
+
+struct sony_924_mode_page_23 { /* track information */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x22 = 34 Bytes */
+ Uchar res_2;
+ Uchar track_num;
+ Uchar data_form;
+ Uchar write_method;
+ Uchar session;
+ Uchar track_status;
+ Uchar start_lba[4];
+ Uchar next_recordable_addr[4];
+ Uchar blank_area_cap[4];
+ Uchar fixed_packet_size[4];
+ Uchar res_24;
+ Uchar starting_msf[3];
+ Uchar res_28;
+ Uchar ending_msf[3];
+ Uchar res_32;
+ Uchar next_rec_time[3];
+};
+
+struct sony_924_mode_page_31 { /* drive speed */
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0x02 = 2 Bytes */
+ Uchar speed;
+ Uchar res;
+};
+
+struct cdd_52x_mode_data {
+ struct scsi_mode_header header;
+ union cdd_pagex {
+ struct sony_924_mode_page_20 page_s20;
+ struct sony_924_mode_page_22 page_s22;
+ struct sony_924_mode_page_23 page_s23;
+ struct sony_924_mode_page_31 page_s31;
+ } pagex;
+};
+
+struct sony_write_parameter {
+ Uchar res0; /* Reserved (must be zero) */
+ Uchar len; /* Parameter length 0x32 == 52 */
+ Uchar res2; /* Reserved (must be zero) */
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+ Ucbit res3_05 : 6; /* Reserved */
+ Ucbit ms : 2; /* Multi session mode */
+#else /* Motorola byteorder */
+ Ucbit ms : 2; /* Multi session mode */
+ Ucbit res3_05 : 6; /* Reserved */
+#endif
+ Uchar resx[12];
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+ Ucbit res16_06 : 7; /* Reserved */
+ Ucbit mcval : 1; /* MCN valid */
+#else /* Motorola byteorder */
+ Ucbit mcval : 1; /* MCN valid */
+ Ucbit res16_06 : 7; /* Reserved */
+#endif
+ Uchar mcn[15];
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+ Ucbit res32_06 : 7; /* Reserved */
+ Ucbit icval : 1; /* ISRC valid */
+#else /* Motorola byteorder */
+ Ucbit icval : 1; /* ISRC valid */
+ Ucbit res32_06 : 7; /* Reserved */
+#endif
+ Uchar isrc[15];
+ Uchar subheader[4];
+};
+
+struct sony_cue {
+ Uchar cs_ctladr; /* CTL/ADR for this track */
+ Uchar cs_tno; /* This track number */
+ Uchar cs_index; /* Index within this track */
+ Uchar cs_dataform; /* Data form */
+ /* Bit 0..5 Main channel Format */
+ /* Bit 6..7 SubChannel format */
+ Uchar cs_zero; /* Reserved or MCN/ISRC */
+ Uchar cs_min; /* Absolute time minutes */
+ Uchar cs_sec; /* Absolute time seconds */
+ Uchar cs_frame; /* Absolute time frames */
+};
+
+
+#define strbeg(s1, s2) (strstr((s2), (s1)) == (s2))
+
+static int write_start_sony(SCSI *usalp, caddr_t bp, int size);
+static int write_continue_sony(SCSI *usalp, caddr_t bp, long sectaddr,
+ long size, int blocks, BOOL islast);
+static int discontinue_sony(SCSI *usalp);
+static int write_track_sony(SCSI *usalp, long track, int sectype);
+static int close_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int flush_sony(SCSI *usalp, int track);
+static int finalize_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int recover_sony(SCSI *usalp, cdr_t *dp, int track);
+static int set_wr_parameter_sony(SCSI *usalp, caddr_t bp, int size);
+static int next_wr_addr_sony(SCSI *usalp, track_t *trackp, long *ap);
+static int reserve_track_sony(SCSI *usalp, unsigned long len);
+static int init_sony(SCSI *usalp, cdr_t *dp);
+static int getdisktype_sony(SCSI *usalp, cdr_t *dp);
+static void di_to_dstat_sony(struct sony_924_mode_page_22 *dip,
+ dstat_t *dsp);
+static int speed_select_sony(SCSI *usalp, cdr_t *dp, int *speedp);
+static int next_writable_address_sony(SCSI *usalp, long *ap, int track,
+ int sectype, int tracktype);
+static int new_track_sony(SCSI *usalp, int track, int sectype,
+ int tracktype);
+static int open_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int open_session_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int abort_session_sony(SCSI *usalp, cdr_t *dp);
+static int get_page22_sony(SCSI *usalp, char *mode);
+static int gen_cue_sony(track_t *trackp, void *vcuep, BOOL needgap);
+static void fillcue(struct sony_cue *cp, int ca, int tno, int idx, int dataform, int scms, msf_t *mp);
+static int send_cue_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int write_leadin_sony(SCSI *usalp, cdr_t *dp, track_t *trackp);
+static int sony_attach(SCSI *usalp, cdr_t *dp);
+#ifdef SONY_DEBUG
+static void print_sony_mp22(struct sony_924_mode_page_22 *xp, int len);
+static void print_sony_mp23(struct sony_924_mode_page_23 *xp, int len);
+#endif
+static int buf_cap_sony(SCSI *usalp, long *, long *);
+
+cdr_t cdr_sony_cdu924 = {
+ 0, 0,
+ CDR_TAO|CDR_SAO|CDR_CADDYLOAD|CDR_SWABAUDIO,
+ CDR_CDRW_NONE,
+ 2, 4,
+ "sony_cdu924",
+ "driver for Sony CDU-924 / CDU-948",
+ 0,
+ (dstat_t *)0,
+ drive_identify,
+ sony_attach,
+ init_sony,
+ getdisktype_sony,
+ scsi_load,
+ scsi_unload,
+ buf_cap_sony,
+ cmd_dummy, /* recovery_needed */
+ recover_sony,
+ speed_select_sony,
+ select_secsize,
+ next_wr_addr_sony,
+ reserve_track_sony,
+ write_continue_sony,
+ gen_cue_sony,
+ send_cue_sony,
+ write_leadin_sony,
+ open_track_sony,
+ close_track_sony,
+ open_session_sony,
+ cmd_dummy,
+ abort_session_sony,
+ read_session_offset_philips,
+ finalize_sony,
+ cmd_dummy, /* stats */
+ blank_dummy,
+ format_dummy,
+ (int(*)(SCSI *, caddr_t, int, int))NULL, /* no OPC */
+ cmd_dummy, /* opt1 */
+ cmd_dummy, /* opt2 */
+};
+
+static int
+write_start_sony(SCSI *usalp, caddr_t bp, int size)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->sense_len = 26;
+ scmd->cdb.g1_cdb.cmd = 0xE0;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, size); /* Hack, but Sony is silly */
+
+ usalp->cmdname = "write_start";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+write_continue_sony(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE1;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, size); /* Hack, but Sony is silly */
+
+ usalp->cmdname = "write_continue";
+
+ if (usal_cmd(usalp) < 0) {
+ /*
+ * XXX This seems to happen only sometimes.
+ */
+ if (usal_sense_code(usalp) != 0x80)
+ return (-1);
+ }
+ return (size - usal_getresid(usalp));
+}
+
+static int
+discontinue_sony(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE2;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "discontinue";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+write_track_sony(SCSI *usalp,
+ long track /* track number 0 == new track */,
+ int sectype /* no sectype for Sony write track */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xF5;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, track);
+
+ usalp->cmdname = "write_track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+/* XXX NOCH NICHT FERTIG */
+static int
+close_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ int track = 0;
+
+ if (!is_tao(trackp) && !is_packet(trackp))
+ return (0);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xF0;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, track);
+/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */
+
+ usalp->cmdname = "close_track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ /*
+ * Clear the silly "error situation" from Sony´ dummy write end
+ * but notify if real errors occurred.
+ */
+ usalp->silent++;
+ if (test_unit_ready(usalp) < 0 && usal_sense_code(usalp) != 0xD4) {
+ usalp->cmdname = "close_track/test_unit_ready";
+ usal_printerr(usalp);
+ }
+ usalp->silent--;
+
+ return (0);
+}
+
+static int
+finalize_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ int dummy = track_base(trackp)->tracktype & TOCF_DUMMY;
+
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ wait_unit_ready(usalp, 240);
+ return (0);
+ }
+ if (dummy) {
+ printf("Fixating is not possible in dummy write mode.\n");
+ return (0);
+ }
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+ scmd->cdb.g1_cdb.cmd = 0xF1;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.count[1] = ((track_base(trackp)->tracktype & TOCF_MULTI) ? 1 : 0);
+/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */
+
+ usalp->cmdname = "finalize";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+flush_sony(SCSI *usalp, int track)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+ scmd->cdb.g1_cdb.cmd = 0xF2;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[5] = track;
+/* XXX POE ??? (bit 1 in addr[0] / CDB[2]) */
+/* XXX Padding ??? (bit 0 in addr[0] / CDB[2]) */
+/* XXX Partial flush ??? (CDB[3]) */
+
+ usalp->cmdname = "flush";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+recover_sony(SCSI *usalp, cdr_t *dp, int track)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xF6;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[3] = track;
+
+ usalp->cmdname = "recover";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+set_wr_parameter_sony(SCSI *usalp, caddr_t bp, int size)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xF8;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, size);
+
+ usalp->cmdname = "set_write_parameter";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+next_wr_addr_sony(SCSI *usalp, track_t *trackp, long *ap)
+{
+ if (next_writable_address_sony(usalp, ap, 0, 0, 0) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+reserve_track_sony(SCSI *usalp, unsigned long len)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xF3;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], len);
+
+ usalp->cmdname = "reserve_track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+static int
+init_sony(SCSI *usalp, cdr_t *dp)
+{
+ return (speed_select_sony(usalp, dp, NULL));
+}
+
+
+#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what);
+
+static int
+getdisktype_sony(SCSI *usalp, cdr_t *dp)
+{
+ dstat_t *dsp = dp->cdr_dstat;
+ long dummy;
+ long lst;
+ msf_t msf;
+
+ char mode[256];
+ struct scsi_mode_page_header *mp;
+ struct sony_924_mode_page_22 *xp;
+
+ dummy = get_page22_sony(usalp, mode);
+ if (dummy >= 0) {
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp = (struct sony_924_mode_page_22 *)mp;
+
+ if (xp->disk_appl_code[0] == 0xFF)
+ dummy = -1;
+ } else {
+ return (drive_getdisktype(usalp, dp));
+ }
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_PRATIP) != 0 && dummy >= 0) {
+
+ printf("ATIP info from disk:\n");
+ printf(" Indicated writing power: %d\n",
+ (unsigned)(xp->disk_appl_code[1] & 0x70) >> 4);
+ IS("unrestricted", xp->disk_appl_code[2] & 0x40);
+ printf(" Disk application code: %d\n", xp->disk_appl_code[2] & 0x3F);
+ msf.msf_min = xp->lead_in_start[1];
+ msf.msf_sec = xp->lead_in_start[2];
+ msf.msf_frame = xp->lead_in_start[3];
+ lst = msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, FALSE);
+ if (lst < -150) {
+ /*
+ * The Sony CDU 920 seems to deliver 00:00/00 for
+ * lead-in start time, dont use it.
+ */
+ printf(" ATIP start of lead in: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, FALSE),
+ msf.msf_min, msf.msf_sec, msf.msf_frame);
+ }
+ msf.msf_min = xp->last_start_time[1];
+ msf.msf_sec = xp->last_start_time[2];
+ msf.msf_frame = xp->last_start_time[3];
+ printf(" ATIP start of lead out: %ld (%02d:%02d/%02d)\n",
+ msf_to_lba(msf.msf_min, msf.msf_sec, msf.msf_frame, TRUE),
+ msf.msf_min, msf.msf_sec, msf.msf_frame);
+ if (lst < -150) {
+ /*
+ * The Sony CDU 920 seems to deliver 00:00/00 for
+ * lead-in start time, dont use it.
+ */
+ msf.msf_min = xp->lead_in_start[1];
+ msf.msf_sec = xp->lead_in_start[2];
+ msf.msf_frame = xp->lead_in_start[3];
+ pr_manufacturer(&msf,
+ FALSE, /* Always not erasable */
+ (xp->disk_appl_code[2] & 0x40) != 0);
+ }
+ }
+ if (dummy >= 0)
+ di_to_dstat_sony(xp, dsp);
+ return (drive_getdisktype(usalp, dp));
+}
+
+static void
+di_to_dstat_sony(struct sony_924_mode_page_22 *dip, dstat_t *dsp)
+{
+ msf_t msf;
+
+ dsp->ds_diskid = a_to_u_4_byte(dip->disk_id_code);
+#ifdef PROTOTYPES
+ if (dsp->ds_diskid != 0xFFFFFFFFUL)
+#else
+ if (dsp->ds_diskid != (Ulong)0xFFFFFFFF)
+#endif
+ dsp->ds_flags |= DSF_DID_V;
+ dsp->ds_diskstat = (dip->disk_status >> 6) & 0x03;
+#ifdef XXX
+ /*
+ * There seems to be no MMC equivalent...
+ */
+ dsp->ds_sessstat = dip->sess_status;
+#endif
+
+ dsp->ds_maxblocks = msf_to_lba(dip->last_start_time[1],
+ dip->last_start_time[2],
+ dip->last_start_time[3], TRUE);
+ /*
+ * Check for 0xFF:0xFF/0xFF which is an indicator for a complete disk
+ */
+ if (dsp->ds_maxblocks == 716730)
+ dsp->ds_maxblocks = -1L;
+
+ if (dsp->ds_first_leadin == 0) {
+ dsp->ds_first_leadin = msf_to_lba(dip->lead_in_start[1],
+ dip->lead_in_start[2],
+ dip->lead_in_start[3], FALSE);
+ /*
+ * Check for illegal values (> 0)
+ * or for empty field (-150) with CDU-920.
+ */
+ if (dsp->ds_first_leadin > 0 || dsp->ds_first_leadin == -150)
+ dsp->ds_first_leadin = 0;
+ }
+
+ if (dsp->ds_last_leadout == 0 && dsp->ds_maxblocks >= 0)
+ dsp->ds_last_leadout = dsp->ds_maxblocks;
+
+ msf.msf_min = dip->lead_in_start[1];
+ msf.msf_sec = dip->lead_in_start[2];
+ msf.msf_frame = dip->lead_in_start[3];
+ dsp->ds_maxrblocks = disk_rcap(&msf, dsp->ds_maxblocks,
+ FALSE, /* Always not erasable */
+ (dip->disk_appl_code[2] & 0x40) != 0);
+}
+
+
+int sony_speeds[] = {
+ -1, /* Speed null is not allowed */
+ 0, /* Single speed */
+ 1, /* Double speed */
+ -1, /* Three times */
+ 3, /* Quad speed */
+};
+
+static int
+speed_select_sony(SCSI *usalp, cdr_t *dp, int *speedp)
+{
+ struct cdd_52x_mode_data md;
+ int count;
+ int err;
+ int speed = 1;
+ BOOL dummy = (dp->cdr_cmdflags & F_DUMMY) != 0;
+
+ if (speedp) {
+ speed = *speedp;
+ if (speed < 1 || speed > 4 || sony_speeds[speed] < 0)
+ return (-1);
+ }
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct sony_924_mode_page_20);
+
+ md.pagex.page_s20.p_code = 0x20;
+ md.pagex.page_s20.p_len = 0x06;
+ md.pagex.page_s20.speudo = dummy?1:0;
+
+ /*
+ * Set Cue sheet option. This is documented for the 924 and
+ * seems to be supported for the 948 too.
+ */
+ md.pagex.page_s20.cue_sheet_opt = 0x03;
+
+ err = mode_select(usalp, (Uchar *)&md, count, 0, 1);
+ if (err < 0)
+ return (err);
+
+ if (speedp == 0)
+ return (0);
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+
+ count = sizeof (struct scsi_mode_header) +
+ sizeof (struct sony_924_mode_page_31);
+
+ md.pagex.page_s31.p_code = 0x31;
+ md.pagex.page_s31.p_len = 0x02;
+ md.pagex.page_s31.speed = sony_speeds[speed];
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, 1));
+}
+
+static int
+next_writable_address_sony(SCSI *usalp, long *ap, int track, int sectype,
+ int tracktype)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int len = 0x30;
+ int page = 0x23;
+ struct sony_924_mode_page_23 *xp;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ inc_verbose();
+ if (!get_mode_params(usalp, page, "CD track information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ dec_verbose();
+ return (-1);
+ }
+ dec_verbose();
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+
+ xp = (struct sony_924_mode_page_23 *)mp;
+
+#ifdef SONY_DEBUG
+ print_sony_mp23(xp, len);
+#endif
+ if (ap)
+ *ap = a_to_4_byte(xp->next_recordable_addr);
+ return (0);
+}
+
+
+static int
+new_track_sony(SCSI *usalp, int track, int sectype, int tracktype)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int len = 0x30;
+ int page = 0x23;
+ struct sony_924_mode_page_23 *xp;
+ int i;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+ get_page22_sony(usalp, mode);
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ inc_verbose();
+ if (!get_mode_params(usalp, page, "CD track information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ dec_verbose();
+ return (-1);
+ }
+ dec_verbose();
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+
+ xp = (struct sony_924_mode_page_23 *)mp;
+
+#ifdef SONY_DEBUG
+ print_sony_mp23(xp, len);
+#endif
+
+ xp->write_method = 0; /* Track at one recording */
+
+ if (sectype & ST_AUDIOMASK) {
+ xp->data_form = (sectype & ST_MASK) == ST_AUDIO_PRE ? 0x02 : 0x00;
+ } else {
+ if (tracktype == TOC_ROM) {
+ xp->data_form = (sectype & ST_MASK) == ST_ROM_MODE1 ? 0x10 : 0x11;
+ } else if (tracktype == TOC_XA1) {
+ xp->data_form = 0x12;
+ } else if (tracktype == TOC_XA2) {
+ xp->data_form = 0x12;
+ } else if (tracktype == TOC_CDI) {
+ xp->data_form = 0x12;
+ }
+ }
+
+ ((struct scsi_modesel_header *)mode)->sense_data_len = 0;
+ ((struct scsi_modesel_header *)mode)->res2 = 0;
+
+ i = ((struct scsi_mode_header *)mode)->blockdesc_len;
+ if (i > 0) {
+ i_to_3_byte(
+ ((struct scsi_mode_data *)mode)->blockdesc.nlblock,
+ 0);
+ }
+
+ if (mode_select(usalp, (Uchar *)mode, len, 0, usalp->inq->data_format >= 2) < 0) {
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+open_track_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ if (!is_tao(trackp) && !is_packet(trackp)) {
+ if (trackp->pregapsize > 0 && (trackp->flags & TI_PREGAP) == 0) {
+ if (lverbose) {
+ printf("Writing pregap for track %d at %ld\n",
+ (int)trackp->trackno,
+ trackp->trackstart-trackp->pregapsize);
+ }
+ /*
+ * XXX Do we need to check isecsize too?
+ */
+ pad_track(usalp, dp, trackp,
+ trackp->trackstart-trackp->pregapsize,
+ (Llong)trackp->pregapsize*trackp->secsize,
+ FALSE, 0);
+ }
+ return (0);
+ }
+
+ if (select_secsize(usalp, trackp->secsize) < 0)
+ return (-1);
+
+ if (new_track_sony(usalp, trackp->trackno, trackp->sectype, trackp->tracktype & TOC_MASK) < 0)
+ return (-1);
+
+ if (write_track_sony(usalp, 0L, trackp->sectype) < 0)
+ return (-1);
+
+ return (0);
+}
+
+static int
+open_session_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct scsi_mode_page_header *mp;
+ char mode[256];
+ int i;
+ int len = 0x30;
+ struct sony_924_mode_page_22 *xp;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if ((len = get_page22_sony(usalp, mode)) < 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp = (struct sony_924_mode_page_22 *)mp;
+
+ xp->disk_type = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+
+ if (is_tao(track_base(trackp))) {
+#ifdef __needed__
+ if ((track_base(trackp)->tracktype & TOC_MASK) == TOC_DA)
+ xp->disk_style = 0x80;
+ else
+ xp->disk_style = 0xC0;
+#endif
+ } else if (is_sao(track_base(trackp))) {
+ /*
+ * We may only change this value if the disk is empty.
+ * i.e. when disk_status & 0xC0 == 0x00
+ */
+ if ((xp->disk_status & 0xC0) != 0) {
+ if (xp->disk_style != 0x00)
+ errmsgno(EX_BAD, "Cannot change disk stile for recorded disk.\n");
+ }
+ xp->disk_style = 0x00;
+ }
+
+ ((struct scsi_modesel_header *)mode)->sense_data_len = 0;
+ ((struct scsi_modesel_header *)mode)->res2 = 0;
+
+ i = ((struct scsi_mode_header *)mode)->blockdesc_len;
+ if (i > 0) {
+ i_to_3_byte(
+ ((struct scsi_mode_data *)mode)->blockdesc.nlblock,
+ 0);
+ }
+
+ if (mode_select(usalp, (Uchar *)mode, len, 0, usalp->inq->data_format >= 2) < 0) {
+ return (-1);
+ }
+/*
+ * XXX set write parameter für SAO mit Multi Session (948 only?)
+ * XXX set_wr_parameter_sony(usalp, bp, size);
+ */
+ return (0);
+}
+
+static int
+abort_session_sony(SCSI *usalp, cdr_t *dp)
+{
+ return (discontinue_sony(usalp));
+}
+
+static int
+get_page22_sony(SCSI *usalp, char *mode)
+{
+ struct scsi_mode_page_header *mp;
+ int len = 0x30;
+ int page = 0x22;
+ struct sony_924_mode_page_22 *xp;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ inc_verbose();
+ if (!get_mode_params(usalp, page, "CD disk information",
+ (Uchar *)mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+ dec_verbose();
+ return (-1);
+ }
+ dec_verbose();
+ if (len == 0)
+ return (-1);
+
+ mp = (struct scsi_mode_page_header *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ xp = (struct sony_924_mode_page_22 *)mp;
+
+#ifdef SONY_DEBUG
+ print_sony_mp22(xp, len);
+#endif
+ return (len);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static Uchar db2df[] = {
+ 0x01, /* 0 2352 bytes of raw data */
+ 0xFF, /* 1 2368 bytes (raw data + P/Q Subchannel) */
+ 0xFF, /* 2 2448 bytes (raw data + P-W Subchannel) */
+ 0xFF, /* 3 2448 bytes (raw data + P-W raw Subchannel)*/
+ 0xFF, /* 4 - Reserved */
+ 0xFF, /* 5 - Reserved */
+ 0xFF, /* 6 - Reserved */
+ 0xFF, /* 7 - Vendor specific */
+ 0x11, /* 8 2048 bytes Mode 1 (ISO/IEC 10149) */
+ 0xFF, /* 9 2336 bytes Mode 2 (ISO/IEC 10149) */
+ 0xFF, /* 10 2048 bytes Mode 2! (CD-ROM XA form 1) */
+ 0xFF, /* 11 2056 bytes Mode 2 (CD-ROM XA form 1) */
+ 0xFF, /* 12 2324 bytes Mode 2 (CD-ROM XA form 2) */
+ 0xFF, /* 13 2332 bytes Mode 2 (CD-ROM XA 1/2+subhdr) */
+ 0xFF, /* 14 - Reserved */
+ 0xFF, /* 15 - Vendor specific */
+};
+
+static int
+gen_cue_sony(track_t *trackp, void *vcuep, BOOL needgap)
+{
+ int tracks = trackp->tracks;
+ int i;
+ struct sony_cue **cuep = vcuep;
+ struct sony_cue *cue;
+ struct sony_cue *cp;
+ int ncue = 0;
+ int icue = 0;
+ int pgsize;
+ msf_t m;
+ int ctl;
+ int df;
+ int scms;
+
+ cue = malloc(1);
+
+ for (i = 0; i <= tracks; i++) {
+ ctl = (st2mode[trackp[i].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[i]))
+ ctl |= TM_ALLOW_COPY << 4;
+ df = db2df[trackp[i].dbtype & 0x0F];
+
+#ifdef __supported__
+ if (trackp[i].isrc) { /* MCN or ISRC */
+ ncue += 2;
+ cue = realloc(cue, ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ if (i == 0) {
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[0], &cp->cs_tno, 7);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x02;
+ movebytes(&trackp[i].isrc[7], &cp->cs_tno, 7);
+ } else {
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[0], &cp->cs_index, 6);
+ cp = &cue[icue++];
+ cp->cs_ctladr = 0x03;
+ cp->cs_tno = i;
+ movebytes(&trackp[i].isrc[6], &cp->cs_index, 6);
+ }
+ }
+#endif
+ if (i == 0) { /* Lead in */
+ df &= ~7;
+ if (trackp[0].flags & TI_TEXT) /* CD-Text in Lead-in*/
+ df |= 0xC0;
+ lba_to_msf(-150, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, 0, &m);
+ } else {
+ scms = 0;
+
+ if (is_scms(&trackp[i]))
+ scms = 0x80;
+ pgsize = trackp[i].pregapsize;
+ if (pgsize == 0 && needgap)
+ pgsize++;
+ lba_to_msf(trackp[i].trackstart-pgsize, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 0, df, scms, &m);
+
+ if (trackp[i].nindex == 1) {
+ lba_to_msf(trackp[i].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, 1, df, scms, &m);
+ } else {
+ int idx;
+ long *idxlist;
+
+ ncue += trackp[i].nindex;
+ idxlist = trackp[i].tindex;
+ cue = realloc(cue, ncue * sizeof (*cue));
+
+ for (idx = 1; idx <= trackp[i].nindex; idx++) {
+ lba_to_msf(trackp[i].trackstart + idxlist[idx], &m);
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, i, idx, df, scms, &m);
+ }
+ }
+ }
+ }
+ /* Lead out */
+ ctl = (st2mode[trackp[tracks+1].sectype & ST_MASK]) << 4;
+ df = db2df[trackp[tracks+1].dbtype & 0x0F];
+ df &= ~7;
+ lba_to_msf(trackp[tracks+1].trackstart, &m);
+ cue = realloc(cue, ++ncue * sizeof (*cue));
+ cp = &cue[icue++];
+ fillcue(cp, ctl|0x01, 0xAA, 1, df, 0, &m);
+
+ if (lverbose > 1) {
+ for (i = 0; i < ncue; i++) {
+ usal_prbytes("", (Uchar *)&cue[i], 8);
+ }
+ }
+ if (cuep)
+ *cuep = cue;
+ else
+ free(cue);
+ return (ncue);
+}
+
+
+static void
+fillcue(struct sony_cue *cp /* The target cue entry */,
+ int ca /* Control/adr for this entry */,
+ int tno /* Track number for this entry */,
+ int idx /* Index for this entry */,
+ int dataform /* Data format for this entry */,
+ int scms /* Serial copy management */,
+ msf_t *mp /* MSF value for this entry */)
+{
+ cp->cs_ctladr = ca;
+ if (tno <= 99)
+ cp->cs_tno = to_bcd(tno);
+ else
+ cp->cs_tno = tno;
+ cp->cs_index = to_bcd(idx);
+ cp->cs_dataform = dataform;
+ cp->cs_zero = scms;
+ cp->cs_min = to_bcd(mp->msf_min);
+ cp->cs_sec = to_bcd(mp->msf_sec);
+ cp->cs_frame = to_bcd(mp->msf_frame);
+}
+
+static int
+send_cue_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ struct sony_cue *cp;
+ int ncue;
+ int ret;
+ Uint i;
+ struct timeval starttime;
+ struct timeval stoptime;
+ int disktype;
+
+ disktype = toc2sess[track_base(trackp)->tracktype & TOC_MASK];
+
+ for (i = 1; i <= trackp->tracks; i++) {
+ if (trackp[i].tracksize < (tsize_t)0) {
+ errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
+ return (-1);
+ }
+ }
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, FALSE);
+
+ starttime.tv_sec = 0;
+ starttime.tv_usec = 0;
+ stoptime = starttime;
+ gettimeofday(&starttime, (struct timezone *)0);
+
+ usalp->silent++;
+ ret = write_start_sony(usalp, (caddr_t)cp, ncue*8);
+ usalp->silent--;
+ free(cp);
+ if (ret < 0) {
+ errmsgno(EX_BAD, "CUE sheet not accepted. Retrying with minimum pregapsize = 1.\n");
+ ncue = (*dp->cdr_gen_cue)(trackp, &cp, TRUE);
+ ret = write_start_sony(usalp, (caddr_t)cp, ncue*8);
+ free(cp);
+ }
+ if (ret >= 0 && lverbose) {
+ gettimeofday(&stoptime, (struct timezone *)0);
+ prtimediff("Write Lead-in time: ", &starttime, &stoptime);
+ }
+ return (ret);
+}
+
+static int
+write_leadin_sony(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ Uint i;
+ long startsec = 0L;
+
+/* if (flags & F_SAO) {*/
+ if (wm_base(dp->cdr_dstat->ds_wrmode) == WM_SAO) {
+ if (debug || lverbose) {
+ printf("Sending CUE sheet...\n");
+ flush();
+ }
+ if (trackp[0].flags & TI_TEXT) {
+ if (dp->cdr_speeddef != 4) {
+ errmsgno(EX_BAD,
+ "The CDU-924 does not support CD-Text, disabling.\n");
+
+ trackp[0].flags &= ~TI_TEXT;
+ }
+ }
+ if ((*dp->cdr_send_cue)(usalp, dp, trackp) < 0) {
+ errmsgno(EX_BAD, "Cannot send CUE sheet.\n");
+ return (-1);
+ }
+
+ if (trackp[0].flags & TI_TEXT) {
+ startsec = dp->cdr_dstat->ds_first_leadin;
+ printf("SAO startsec: %ld\n", startsec);
+ } else {
+ startsec = -150;
+ }
+ if (debug)
+ printf("SAO startsec: %ld\n", startsec);
+
+ if (trackp[0].flags & TI_TEXT) {
+ if (startsec > 0) {
+ errmsgno(EX_BAD, "CD-Text must be in first session.\n");
+ return (-1);
+ }
+ if (debug || lverbose)
+ printf("Writing lead-in...\n");
+ if (write_cdtext(usalp, dp, startsec) < 0)
+ return (-1);
+
+ dp->cdr_dstat->ds_cdrflags |= RF_LEADIN;
+ } else for (i = 1; i <= trackp->tracks; i++) {
+ trackp[i].trackstart += startsec +150;
+ }
+ }
+ return (0);
+}
+
+/*--------------------------------------------------------------------------*/
+
+static const char *sd_cdu_924_error_str[] = {
+
+ "\200\000write complete", /* 80 00 */
+ "\201\000logical unit is reserved", /* 81 00 */
+ "\205\000audio address not valid", /* 85 00 */
+ "\210\000illegal cue sheet", /* 88 00 */
+ "\211\000inappropriate command", /* 89 00 */
+
+ "\266\000media load mechanism failed", /* B6 00 */
+ "\271\000audio play operation aborted", /* B9 00 */
+ "\277\000buffer overflow for read all subcodes command", /* BF 00 */
+ "\300\000unrecordable disk", /* C0 00 */
+ "\301\000illegal track status", /* C1 00 */
+ "\302\000reserved track present", /* C2 00 */
+ "\303\000buffer data size error", /* C3 00 */
+ "\304\001illegal data form for reserve track command", /* C4 01 */
+ "\304\002unable to reserve track, because track mode has been changed", /* C4 02 */
+ "\305\000buffer error during at once recording", /* C5 00 */
+ "\306\001unwritten area encountered", /* C6 01 */
+ "\306\002link blocks encountered", /* C6 02 */
+ "\306\003nonexistent block encountered", /* C6 03 */
+ "\307\000disk style mismatch", /* C7 00 */
+ "\310\000no table of contents", /* C8 00 */
+ "\311\000illegal block length for write command", /* C9 00 */
+ "\312\000power calibration error", /* CA 00 */
+ "\313\000write error", /* CB 00 */
+ "\313\001write error track recovered", /* CB 01 */
+ "\314\000not enough space", /* CC 00 */
+ "\315\000no track present to finalize", /* CD 00 */
+ "\316\000unrecoverable track descriptor encountered", /* CE 00 */
+ "\317\000damaged track present", /* CF 00 */
+ "\320\000pma area full", /* D0 00 */
+ "\321\000pca area full", /* D1 00 */
+ "\322\000unrecoverable damaged track cause too small writing area", /* D2 00 */
+ "\323\000no bar code", /* D3 00 */
+ "\323\001not enough bar code margin", /* D3 01 */
+ "\323\002no bar code start pattern", /* D3 02 */
+ "\323\003illegal bar code length", /* D3 03 */
+ "\323\004illegal bar code format", /* D3 04 */
+ "\324\000exit from pseudo track at once recording", /* D4 00 */
+ NULL
+};
+
+static int
+sony_attach(SCSI *usalp, cdr_t *dp)
+{
+ if (usalp->inq != NULL) {
+ if (strbeg("CD-R CDU94", usalp->inq->prod_ident)) {
+ dp->cdr_speeddef = 4;
+ }
+ }
+ usal_setnonstderrs(usalp, sd_cdu_924_error_str);
+ return (0);
+}
+
+#ifdef SONY_DEBUG
+static void
+print_sony_mp22(struct sony_924_mode_page_22 *xp, int len)
+{
+ printf("disk style: %X\n", xp->disk_style);
+ printf("disk type: %X\n", xp->disk_type);
+ printf("first track: %X\n", xp->first_track);
+ printf("last track: %X\n", xp->last_track);
+ printf("numsess: %X\n", xp->numsess);
+ printf("disk appl code: %lX\n", a_to_u_4_byte(xp->disk_appl_code));
+ printf("last start time: %lX\n", a_to_u_4_byte(xp->last_start_time));
+ printf("disk status: %X\n", xp->disk_status);
+ printf("num valid nra: %X\n", xp->num_valid_nra);
+ printf("track info track: %X\n", xp->track_info_track);
+ printf("post gap: %X\n", xp->post_gap);
+ printf("disk id code: %lX\n", a_to_u_4_byte(xp->disk_id_code));
+ printf("lead in start: %lX\n", a_to_u_4_byte(xp->lead_in_start));
+}
+
+static void
+print_sony_mp23(struct sony_924_mode_page_23 *xp, int len)
+{
+ printf("len: %d\n", len);
+
+ printf("track num: %X\n", xp->track_num);
+ printf("data form: %X\n", xp->data_form);
+ printf("write method: %X\n", xp->write_method);
+ printf("session: %X\n", xp->session);
+ printf("track status: %X\n", xp->track_status);
+
+/*
+ * XXX Check for signed/unsigned a_to_*() conversion.
+ */
+ printf("start lba: %lX\n", a_to_4_byte(xp->start_lba));
+ printf("next recordable addr: %lX\n", a_to_4_byte(xp->next_recordable_addr));
+ printf("blank area cap: %lX\n", a_to_u_4_byte(xp->blank_area_cap));
+ printf("fixed packet size: %lX\n", a_to_u_4_byte(xp->fixed_packet_size));
+ printf("starting msf: %lX\n", a_to_u_4_byte(xp->starting_msf));
+ printf("ending msf: %lX\n", a_to_u_4_byte(xp->ending_msf));
+ printf("next rec time: %lX\n", a_to_u_4_byte(xp->next_rec_time));
+}
+#endif
+
+static int
+buf_cap_sony(SCSI *usalp, long *sp, long *fp)
+{
+ char resp[8];
+ Ulong freespace;
+ Ulong bufsize;
+ int per;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)resp;
+ scmd->size = sizeof (resp);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xEC; /* Read buffer cap */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "read buffer cap sony";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ bufsize = a_to_u_3_byte(&resp[1]);
+ freespace = a_to_u_3_byte(&resp[5]);
+ if (sp)
+ *sp = bufsize;
+ if (fp)
+ *fp = freespace;
+
+ if (usalp->verbose || (sp == 0 && fp == 0))
+ printf("BFree: %ld K BSize: %ld K\n", freespace >> 10, bufsize >> 10);
+
+ if (bufsize == 0)
+ return (0);
+ per = (100 * (bufsize - freespace)) / bufsize;
+ if (per < 0)
+ return (0);
+ if (per > 100)
+ return (100);
+ return (per);
+}
diff --git a/wodim/fifo.c b/wodim/fifo.c
new file mode 100644
index 0000000..2f70f5b
--- /dev/null
+++ b/wodim/fifo.c
@@ -0,0 +1,878 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)fifo.c 1.49 06/02/08 Copyright 1989,1997-2006 J. Schilling */
+/*
+ * A "fifo" that uses shared memory between two processes
+ *
+ * The actual code is a mixture of borrowed code from star's fifo.c
+ * and a proposal from Finn Arne Gangstad <finnag@guardian.no>
+ * who had the idea to use a ring buffer to handle average size chunks.
+ *
+ * Copyright (c) 1989,1997-2006 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+/*#define XDEBUG*/
+#include <mconfig.h>
+
+ /* We always wish FIFO unless it is disabled below */
+#ifndef FIFO
+#define FIFO
+#endif
+
+#if defined(HAVE_OS_H) && \
+ defined(HAVE_CLONE_AREA) && defined(HAVE_CREATE_AREA) && \
+ defined(HAVE_DELETE_AREA)
+#include <OS.h>
+# define HAVE_BEOS_AREAS /* BeOS/Zeta */
+#endif
+#if !defined(HAVE_SMMAP) && !defined(HAVE_USGSHM) && \
+ !defined(HAVE_DOSALLOCSHAREDMEM) && !defined(HAVE_BEOS_AREAS)
+#undef FIFO /* We cannot have a FIFO on this platform */
+#endif
+#if !defined(HAVE_FORK)
+#undef FIFO /* We cannot have a FIFO on this platform */
+#endif
+#ifdef FIFO
+#if !defined(USE_MMAP) && !defined(USE_USGSHM)
+#define USE_MMAP
+#endif
+#ifndef HAVE_SMMAP
+# undef USE_MMAP
+# define USE_USGSHM /* now SYSV shared memory is the default*/
+#endif
+#ifdef USE_MMAP /* Only want to have one implementation */
+# undef USE_USGSHM /* mmap() is preferred */
+#endif
+
+#ifdef HAVE_DOSALLOCSHAREDMEM /* This is for OS/2 */
+# undef USE_MMAP
+# undef USE_USGSHM
+# define USE_OS2SHM
+#endif
+
+#ifdef HAVE_BEOS_AREAS /* This is for BeOS/Zeta */
+# undef USE_MMAP
+# undef USE_USGSHM
+# undef USE_OS2SHM
+# define USE_BEOS_AREAS
+#endif
+
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h> /* includes <sys/types.h> */
+#include <utypes.h>
+#include <fctldefs.h>
+#if defined(HAVE_SMMAP) && defined(USE_MMAP)
+#include <mmapdefs.h>
+#endif
+#include <waitdefs.h>
+#include <standard.h>
+#include <errno.h>
+#include <signal.h>
+#include <libport.h>
+#include <schily.h>
+
+#include "wodim.h"
+#include "xio.h"
+
+#ifdef DEBUG
+#ifdef XDEBUG
+FILE *ef;
+#define USDEBUG1 if (debug) {if (s == owner_reader) fprintf(ef, "r"); else fprintf(ef, "w"); fflush(ef); }
+#define USDEBUG2 if (debug) {if (s == owner_reader) fprintf(ef, "R"); else fprintf(ef, "W"); fflush(ef); }
+#else
+#define USDEBUG1
+#define USDEBUG2
+#endif
+#define EDEBUG(a) if (debug) schily_error a
+#else
+#define EDEBUG(a)
+#define USDEBUG1
+#define USDEBUG2
+#endif
+
+#define palign(x, a) (((char *)(x)) + ((a) - 1 - (((UIntptr_t)((x)-1))%(a))))
+
+typedef enum faio_owner {
+ owner_none, /* Unused in real life */
+ owner_writer, /* owned by process that writes into FIFO */
+ owner_faio, /* Intermediate state when buf still in use */
+ owner_reader /* owned by process that reads from FIFO */
+} fowner_t;
+
+char *onames[] = {
+ "none",
+ "writer",
+ "faio",
+ "reader",
+};
+
+typedef struct faio {
+ int len;
+ volatile fowner_t owner;
+ volatile int users;
+ short fd;
+ short saved_errno;
+ char *bufp;
+} faio_t;
+
+struct faio_stats {
+ long puts;
+ long gets;
+ long empty;
+ long full;
+ long done;
+ long cont_low;
+ int users;
+} *sp;
+
+#define MIN_BUFFERS 3
+
+#define MSECS 1000
+#define SECS (1000*MSECS)
+
+/*
+ * Note: WRITER_MAXWAIT & READER_MAXWAIT need to be greater than the SCSI
+ * timeout for commands that write to the media. This is currently 200s
+ * if we are in SAO mode.
+ */
+/* microsecond delay between each buffer-ready probe by writing process */
+#define WRITER_DELAY (20*MSECS)
+#define WRITER_MAXWAIT (240*SECS) /* 240 seconds max wait for data */
+
+/* microsecond delay between each buffer-ready probe by reading process */
+#define READER_DELAY (80*MSECS)
+#define READER_MAXWAIT (240*SECS) /* 240 seconds max wait for reader */
+
+static char *buf;
+static char *bufbase;
+static char *bufend;
+static long buflen; /* The size of the FIFO buffer */
+
+extern int debug;
+extern int lverbose;
+
+void init_fifo(long);
+#ifdef USE_MMAP
+static char *mkshare(int size);
+#endif
+#ifdef USE_USGSHM
+static char *mkshm(int size);
+#endif
+#ifdef USE_OS2SHM
+static char *mkos2shm(int size);
+#endif
+#ifdef USE_BEOS_AREAS
+static char *mkbeosshm(int size);
+static void beosshm_child(void);
+#endif
+
+BOOL init_faio(track_t *trackp, int);
+BOOL await_faio(void);
+void kill_faio(void);
+int wait_faio(void);
+static void faio_reader(track_t *trackp);
+static void faio_read_track(track_t *trackp);
+static void faio_wait_on_buffer(faio_t *f, fowner_t s, unsigned long delay,
+ unsigned long max_wait);
+static int faio_read_segment(int fd, faio_t *f, track_t *track, long secno,
+ int len);
+static faio_t *faio_ref(int n);
+int faio_read_buf(int f, char *bp, int size);
+int faio_get_buf(int f, char **bpp, int size);
+void fifo_stats(void);
+int fifo_percent(BOOL addone);
+
+
+void
+init_fifo(long fs)
+{
+ int pagesize;
+
+ if (fs == 0L)
+ return;
+
+ pagesize = getpagesize();
+ buflen = roundup(fs, pagesize) + pagesize;
+ EDEBUG(("fs: %ld buflen: %ld\n", fs, buflen));
+
+#if defined(USE_MMAP)
+ buf = mkshare(buflen);
+#endif
+#if defined(USE_USGSHM)
+ buf = mkshm(buflen);
+#endif
+#if defined(USE_OS2SHM)
+ buf = mkos2shm(buflen);
+#endif
+#if defined(USE_BEOS_AREAS)
+ buf = mkbeosshm(buflen);
+#endif
+
+ bufbase = buf;
+ bufend = buf + buflen;
+ EDEBUG(("buf: %p bufend: %p, buflen: %ld\n", buf, bufend, buflen));
+ buf = palign(buf, pagesize);
+ buflen -= buf - bufbase;
+ EDEBUG(("buf: %p bufend: %p, buflen: %ld (align %ld)\n", buf, bufend, buflen, (long)(buf - bufbase)));
+
+ /*
+ * Dirty the whole buffer. This can die with various signals if
+ * we're trying to lock too much memory
+ */
+ fillbytes(buf, buflen, '\0');
+
+#ifdef XDEBUG
+ if (debug)
+ ef = fopen("/tmp/ef", "w");
+#endif
+}
+
+#ifdef USE_MMAP
+static char *
+mkshare(int size)
+{
+ int f;
+ char *addr;
+
+#ifdef MAP_ANONYMOUS /* HP/UX */
+ f = -1;
+ addr = mmap(0, mmap_sizeparm(size),
+ PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, f, 0);
+#else
+ if ((f = open("/dev/zero", O_RDWR)) < 0)
+ comerr("Cannot open '/dev/zero'.\n");
+ addr = mmap(0, mmap_sizeparm(size),
+ PROT_READ|PROT_WRITE, MAP_SHARED, f, 0);
+#endif
+ if (addr == (char *)-1)
+ comerr("Cannot get mmap for %d Bytes on /dev/zero.\n", size);
+ if (f >= 0)
+ close(f);
+
+ if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
+ (void *)addr, size);
+
+ return (addr);
+}
+#endif
+
+#ifdef USE_USGSHM
+#include <sys/ipc.h>
+#include <sys/shm.h>
+static char *
+mkshm(int size)
+{
+ int id;
+ char *addr;
+ /*
+ * Unfortunately, a declaration of shmat() is missing in old
+ * implementations such as AT&T SVr0 and SunOS.
+ * We cannot add this definition here because the return-type
+ * changed on newer systems.
+ *
+ * We will get a warning like this:
+ *
+ * warning: assignment of pointer from integer lacks a cast
+ * or
+ * warning: illegal combination of pointer and integer, op =
+ */
+/* extern char *shmat();*/
+
+ if ((id = shmget(IPC_PRIVATE, size, IPC_CREAT|0600)) == -1)
+ comerr("shmget failed\n");
+
+ if (debug) errmsgno(EX_BAD, "shared memory segment allocated: %d\n", id);
+
+ if ((addr = shmat(id, (char *)0, 0600)) == (char *)-1)
+ comerr("shmat failed\n");
+
+ if (debug) errmsgno(EX_BAD, "shared memory segment attached at: %p size %d\n",
+ (void *)addr, size);
+
+ if (shmctl(id, IPC_RMID, 0) < 0)
+ comerr("shmctl failed to detach shared memory segment\n");
+
+#ifdef SHM_LOCK
+ /*
+ * Although SHM_LOCK is standard, it seems that all versions of AIX
+ * ommit this definition.
+ */
+ if (shmctl(id, SHM_LOCK, 0) < 0)
+ comerr("shmctl failed to lock shared memory segment\n");
+#endif
+
+ return (addr);
+}
+#endif
+
+#ifdef USE_OS2SHM
+static char *
+mkos2shm(int size)
+{
+ char *addr;
+
+ /*
+ * The OS/2 implementation of shm (using shm.dll) limits the size of one shared
+ * memory segment to 0x3fa000 (aprox. 4MBytes). Using OS/2 native API we have
+ * no such restriction so I decided to use it allowing fifos of arbitrary size.
+ */
+ if (DosAllocSharedMem(&addr, NULL, size, 0X100L | 0x1L | 0x2L | 0x10L))
+ comerr("DosAllocSharedMem() failed\n");
+
+ if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
+ (void *)addr, size);
+
+ return (addr);
+}
+#endif
+
+#ifdef USE_BEOS_AREAS
+static area_id faio_aid;
+static void *faio_addr;
+static char faio_name[32];
+
+static char *
+mkbeosshm(int size)
+{
+ snprintf(faio_name, sizeof (faio_name), "cdrecord FIFO %lld",
+ (Llong)getpid());
+
+ faio_aid = create_area(faio_name, &faio_addr,
+ B_ANY_ADDRESS,
+ size,
+ B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
+ if (faio_addr == NULL) {
+ comerrno(faio_aid,
+ "Cannot get create_area for %d Bytes FIFO.\n", size);
+ }
+ if (debug) errmsgno(EX_BAD, "shared memory allocated attached at: %p size %d\n",
+ (void *)faio_addr, size);
+ return (faio_addr);
+}
+
+static void
+beosshm_child()
+{
+ /*
+ * Delete the area created by fork that is copy-on-write.
+ */
+ delete_area(area_for(faio_addr));
+ /*
+ * Clone (share) the original one.
+ */
+ faio_aid = clone_area(faio_name, &faio_addr,
+ B_ANY_ADDRESS, B_READ_AREA|B_WRITE_AREA,
+ faio_aid);
+ if (bufbase != faio_addr) {
+ errmsgno(EX_BAD, "Panic FIFO addr.\n");
+ return (FALSE);
+ }
+}
+#endif
+
+static int faio_buffers;
+static int faio_buf_size;
+static int buf_idx = 0; /* Initialize to fix an Amiga bug */
+static int buf_idx_reader = 0; /* Separate var to allow vfork() */
+ /* buf_idx_reader is for the process */
+ /* that fills the FIFO */
+static pid_t faio_pid = -1;
+static BOOL faio_didwait;
+
+#ifdef AMIGA
+/*
+ * On Amiga fork will be replaced by the speciall vfork() like call ix_vfork,
+ * which lets the parent asleep. The child process later wakes up the parent
+ * process by calling ix_fork_resume().
+ */
+#define fork() ix_vfork()
+#define __vfork_resume() ix_vfork_resume()
+
+#else /* !AMIGA */
+#define __vfork_resume()
+#endif
+
+
+/*#define faio_ref(n) (&((faio_t *)buf)[n])*/
+
+
+BOOL
+init_faio(track_t *trackp, int bufsize)
+{
+ int n;
+ faio_t *f;
+ int pagesize;
+ char *base;
+
+ if (buflen == 0L)
+ return (FALSE);
+
+ pagesize = getpagesize();
+ faio_buf_size = bufsize;
+ f = (faio_t *)buf;
+
+ /*
+ * Compute space for buffer headers.
+ * Round bufsize up to pagesize to make each FIFO segment
+ * properly page aligned.
+ */
+ bufsize = roundup(bufsize, pagesize);
+ faio_buffers = (buflen - sizeof (*sp)) / bufsize;
+ EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
+
+ /*
+ * Reduce buffer space by header space.
+ */
+ n = sizeof (*sp) + faio_buffers * sizeof (struct faio);
+ n = roundup(n, pagesize);
+ faio_buffers = (buflen-n) / bufsize;
+ EDEBUG(("bufsize: %d buffers: %d hdrsize %ld\n", bufsize, faio_buffers, (long)faio_buffers * sizeof (struct faio)));
+
+ if (faio_buffers < MIN_BUFFERS) {
+ errmsgno(EX_BAD,
+ "write-buffer too small, minimum is %dk. Disabling.\n",
+ MIN_BUFFERS*bufsize/1024);
+ return (FALSE);
+ }
+
+ if (debug)
+ printf("Using %d buffers of %d bytes.\n", faio_buffers, faio_buf_size);
+
+ f = (faio_t *)buf;
+ base = buf + roundup(sizeof (*sp) + faio_buffers * sizeof (struct faio),
+ pagesize);
+
+ for (n = 0; n < faio_buffers; n++, f++, base += bufsize) {
+ /* Give all the buffers to the file reader process */
+ f->owner = owner_writer;
+ f->users = 0;
+ f->bufp = base;
+ f->fd = -1;
+ }
+ sp = (struct faio_stats *)f; /* point past headers */
+ sp->gets = sp->puts = sp->done = 0L;
+ sp->users = 1;
+
+ faio_pid = fork();
+ if (faio_pid < 0)
+ comerr("fork(2) failed");
+
+ if (faio_pid == 0) {
+ /*
+ * child (background) process that fills the FIFO.
+ */
+ raisepri(1); /* almost max priority */
+
+#ifdef USE_OS2SHM
+ DosGetSharedMem(buf, 3); /* PAG_READ|PAG_WRITE */
+#endif
+#ifdef USE_BEOS_AREAS
+ beosshm_child();
+#endif
+ /* Ignoring SIGALRM cures the SCO usleep() bug */
+/* signal(SIGALRM, SIG_IGN);*/
+ __vfork_resume(); /* Needed on some platforms */
+ faio_reader(trackp);
+ /* NOTREACHED */
+ } else {
+#ifdef __needed__
+ Uint t;
+#endif
+
+ faio_didwait = FALSE;
+
+ /*
+ * XXX We used to close all track files in the foreground
+ * XXX process. This was not correct before we used "xio"
+ * XXX and with "xio" it will start to fail because we need
+ * XXX the fd handles for the faio_get_buf() function.
+ */
+#ifdef __needed__
+ /* close all file-descriptors that only the child will use */
+ for (t = 1; t <= trackp->tracks; t++) {
+ if (trackp[t].xfp != NULL)
+ xclose(trackp[t].xfp);
+ }
+#endif
+ }
+
+ return (TRUE);
+}
+
+BOOL
+await_faio()
+{
+ int n;
+ int lastfd = -1;
+ faio_t *f;
+
+ /*
+ * Wait until the reader is active and has filled the buffer.
+ */
+ if (lverbose || debug) {
+ printf("Waiting for reader process to fill input buffer ... ");
+ flush();
+ }
+
+ faio_wait_on_buffer(faio_ref(faio_buffers - 1), owner_reader,
+ 500*MSECS, 0);
+
+ if (lverbose || debug)
+ printf("input buffer ready.\n");
+
+ sp->empty = sp->full = 0L; /* set correct stat state */
+ sp->cont_low = faio_buffers; /* set cont to max value */
+
+ f = faio_ref(0);
+ for (n = 0; n < faio_buffers; n++, f++) {
+ if (f->fd != lastfd &&
+ f->fd == STDIN_FILENO && f->len == 0) {
+ errmsgno(EX_BAD, "Premature EOF on stdin.\n");
+ kill(faio_pid, SIGKILL);
+ return (FALSE);
+ }
+ lastfd = f->fd;
+ }
+ return (TRUE);
+}
+
+void
+kill_faio()
+{
+ if (faio_pid > 0)
+ kill(faio_pid, SIGKILL);
+ faio_pid=-1;
+}
+
+int
+wait_faio()
+{
+ if (faio_pid > 0 && !faio_didwait)
+ return (wait(0));
+ faio_didwait = TRUE;
+ return (0);
+}
+
+static void
+faio_reader(track_t *trackp)
+{
+ /* This function should not return, but _exit. */
+ Uint trackno;
+
+ if (debug)
+ printf("\nfaio_reader starting\n");
+
+ for (trackno = 1; trackno <= trackp->tracks; trackno++) {
+ if (debug)
+ printf("\nfaio_reader reading track %u\n", trackno);
+ faio_read_track(&trackp[trackno]);
+ }
+ sp->done++;
+ if (debug)
+ printf("\nfaio_reader all tracks read, exiting\n");
+
+ /* Prevent hang if buffer is larger than all the tracks combined */
+ if (sp->gets == 0)
+ faio_ref(faio_buffers - 1)->owner = owner_reader;
+
+#ifdef USE_OS2SHM
+ DosFreeMem(buf);
+ sleep(30000); /* XXX If calling _exit() here the parent process seems to be blocked */
+ /* XXX This should be fixed soon */
+#endif
+ if (debug)
+ fprintf(stderr, "\nfaio_reader _exit(0)\n");
+ _exit(0);
+}
+
+#ifndef faio_ref
+static faio_t *
+faio_ref(int n)
+{
+ return (&((faio_t *)buf)[n]);
+}
+#endif
+
+
+static void
+faio_read_track(track_t *trackp)
+{
+ int fd = -1;
+ int bytespt = trackp->secsize * trackp->secspt;
+ int secspt = trackp->secspt;
+ int l;
+ long secno = trackp->trackstart;
+ tsize_t tracksize = trackp->tracksize;
+ tsize_t bytes_read = (tsize_t)0;
+ long bytes_to_read;
+
+ if (trackp->xfp != NULL)
+ fd = xfileno(trackp->xfp);
+
+ if (bytespt > faio_buf_size) {
+ comerrno(EX_BAD,
+ "faio_read_track fatal: secsize %d secspt %d, bytespt(%d) > %d !!\n",
+ trackp->secsize, trackp->secspt, bytespt,
+ faio_buf_size);
+ }
+
+ do {
+ bytes_to_read = bytespt;
+ if (tracksize > 0) {
+ if ((tracksize - bytes_read) > bytespt) {
+ bytes_to_read = bytespt;
+ } else {
+ bytes_to_read = tracksize - bytes_read;
+ }
+ }
+ l = faio_read_segment(fd, faio_ref(buf_idx_reader), trackp, secno, bytes_to_read);
+ if (++buf_idx_reader >= faio_buffers)
+ buf_idx_reader = 0;
+ if (l <= 0)
+ break;
+ bytes_read += l;
+ secno += secspt;
+ } while (tracksize < 0 || bytes_read < tracksize);
+
+ xclose(trackp->xfp); /* Don't keep files open longer than neccesary */
+}
+
+static void
+#ifdef PROTOTYPES
+faio_wait_on_buffer(faio_t *f, fowner_t s,
+ unsigned long delay,
+ unsigned long max_wait)
+#else
+faio_wait_on_buffer(faio_t *f, fowner_t *s, unsigned long delay, unsigned long max_wait)
+#endif
+{
+ unsigned long max_loops;
+
+ if (f->owner == s)
+ return; /* return immediately if the buffer is ours */
+
+ if (s == owner_reader)
+ sp->empty++;
+ else
+ sp->full++;
+
+ max_loops = max_wait / delay + 1;
+
+ while (max_wait == 0 || max_loops--) {
+ USDEBUG1;
+ usleep(delay);
+ USDEBUG2;
+
+ if (f->owner == s)
+ return;
+ }
+ if (debug) {
+ errmsgno(EX_BAD,
+ "%lu microseconds passed waiting for %d current: %d idx: %ld\n",
+ max_wait, s, f->owner, (long)(f - faio_ref(0))/sizeof (*f));
+ }
+ comerrno(EX_BAD, "faio_wait_on_buffer for %s timed out.\n",
+ (s > owner_reader || s < owner_none) ? "bad_owner" : onames[s-owner_none]);
+}
+
+static int
+faio_read_segment(int fd, faio_t *f, track_t *trackp, long secno, int len)
+{
+ int l;
+
+ faio_wait_on_buffer(f, owner_writer, WRITER_DELAY, WRITER_MAXWAIT);
+
+ f->fd = fd;
+ l = fill_buf(fd, trackp, secno, f->bufp, len);
+ f->len = l;
+ f->saved_errno = geterrno();
+ f->owner = owner_reader;
+ f->users = sp->users;
+
+ sp->puts++;
+
+ return (l);
+}
+
+int
+faio_read_buf(int fd, char *bp, int size)
+{
+ char *bufp;
+
+ int len = faio_get_buf(fd, &bufp, size);
+ if (len > 0) {
+ movebytes(bufp, bp, len);
+ }
+ return (len);
+}
+
+int
+faio_get_buf(int fd, char **bpp, int size)
+{
+ faio_t *f;
+ int len;
+
+again:
+ f = faio_ref(buf_idx);
+ if (f->owner == owner_faio) {
+ f->owner = owner_writer;
+ if (++buf_idx >= faio_buffers)
+ buf_idx = 0;
+ f = faio_ref(buf_idx);
+ }
+
+ if ((sp->puts - sp->gets) < sp->cont_low && sp->done == 0) {
+ EDEBUG(("gets: %ld puts: %ld cont: %ld low: %ld\n", sp->gets, sp->puts, sp->puts - sp->gets, sp->cont_low));
+ sp->cont_low = sp->puts - sp->gets;
+ }
+ faio_wait_on_buffer(f, owner_reader, READER_DELAY, READER_MAXWAIT);
+ len = f->len;
+
+ if (f->fd != fd) {
+ if (f->len == 0) {
+ /*
+ * If the tracksize for this track was known, and
+ * the tracksize is 0 mod bytespt, this happens.
+ */
+ goto again;
+ }
+ comerrno(EX_BAD,
+ "faio_get_buf fatal: fd=%d, f->fd=%d, f->len=%d f->errno=%d\n",
+ fd, f->fd, f->len, f->saved_errno);
+ }
+ if (size < len) {
+ comerrno(EX_BAD,
+ "unexpected short read-attempt in faio_get_buf. size = %d, len = %d\n",
+ size, len);
+ }
+
+ if (len < 0)
+ seterrno(f->saved_errno);
+
+ sp->gets++;
+
+ *bpp = f->bufp;
+ if (--f->users <= 0)
+ f->owner = owner_faio;
+ return (len);
+}
+
+void
+fifo_stats()
+{
+ if (sp == NULL) /* We might not use a FIFO */
+ return;
+
+ errmsgno(EX_BAD, "fifo had %ld puts and %ld gets.\n",
+ sp->puts, sp->gets);
+ errmsgno(EX_BAD, "fifo was %ld times empty and %ld times full, min fill was %ld%%.\n",
+ sp->empty, sp->full, (100L*sp->cont_low)/faio_buffers);
+}
+
+int
+fifo_percent(BOOL addone)
+{
+ int percent;
+
+ if (sp == NULL) /* We might not use a FIFO */
+ return (-1);
+
+ if (sp->done)
+ return (100);
+ percent = (100*(sp->puts + 1 - sp->gets)/faio_buffers);
+ if (percent > 100)
+ return (100);
+ return (percent);
+}
+#else /* FIFO */
+
+#include <standard.h>
+#include <utypes.h> /* includes sys/types.h */
+#include <schily.h>
+
+#include "wodim.h"
+
+void init_fifo(long);
+BOOL init_faio(track_t *track, int);
+BOOL await_faio(void);
+void kill_faio(void);
+int wait_faio(void);
+int faio_read_buf(int f, char *bp, int size);
+int faio_get_buf(int f, char **bpp, int size);
+void fifo_stats(void);
+int fifo_percent(BOOL addone);
+
+
+void init_fifo(long fs)
+{
+ errmsgno(EX_BAD, "Fifo not supported.\n");
+}
+
+BOOL init_faio(track_t *track,
+ int bufsize /* The size of a single transfer buffer */)
+{
+ return (FALSE);
+}
+
+BOOL await_faio()
+{
+ return (TRUE);
+}
+
+void kill_faio()
+{
+}
+
+int wait_faio()
+{
+ return (0);
+}
+
+int faio_read_buf(int fd, char *bp, int size)
+{
+ return (0);
+}
+
+int faio_get_buf(int fd, char **bpp, int size)
+{
+ return (0);
+}
+
+void fifo_stats()
+{
+}
+
+int fifo_percent(BOOL addone)
+{
+ return (-1);
+}
+
+#endif /* FIFO */
diff --git a/wodim/getnum.c b/wodim/getnum.c
new file mode 100644
index 0000000..ed13df9
--- /dev/null
+++ b/wodim/getnum.c
@@ -0,0 +1,123 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)getnum.c 1.2 04/03/02 Copyright 1984-2002, 2004 J. Schilling */
+/*
+ * Number conversion routines to implement 'dd' like options.
+ *
+ * Copyright (c) 1984-2002, 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <standard.h>
+#include <utypes.h>
+#include <schily.h>
+
+static Llong number(char *arg, int *retp);
+int getnum(char *arg, long *valp);
+int getllnum(char *arg, Llong *lvalp);
+
+static Llong
+number(register char *arg, int *retp)
+{
+ Llong val = 0;
+
+ if (*retp != 1)
+ return (val);
+ if (*arg == '\0') {
+ *retp = -1;
+ } else if (*(arg = astoll(arg, &val))) {
+ if (*arg == 'p' || *arg == 'P') {
+ val *= (1024*1024);
+ val *= (1024*1024*1024);
+ arg++;
+
+ } else if (*arg == 't' || *arg == 'T') {
+ val *= (1024*1024);
+ val *= (1024*1024);
+ arg++;
+
+ } else if (*arg == 'g' || *arg == 'G') {
+ val *= (1024*1024*1024);
+ arg++;
+
+ } else if (*arg == 'm' || *arg == 'M') {
+ val *= (1024*1024);
+ arg++;
+
+ } else if (*arg == 'f' || *arg == 'F') {
+ val *= 2352;
+ arg++;
+
+ } else if (*arg == 's' || *arg == 'S') {
+ val *= 2048;
+ arg++;
+
+ } else if (*arg == 'k' || *arg == 'K') {
+ val *= 1024;
+ arg++;
+
+ } else if (*arg == 'b' || *arg == 'B') {
+ val *= 512;
+ arg++;
+
+ } else if (*arg == 'w' || *arg == 'W') {
+ val *= 2;
+ arg++;
+ }
+ if (*arg == '*' || *arg == 'x')
+ val *= number(++arg, retp);
+ else if (*arg != '\0')
+ *retp = -1;
+ }
+ return (val);
+}
+
+int
+getnum(char *arg, long *valp)
+{
+ Llong llval;
+ int ret = 1;
+
+ llval = number(arg, &ret);
+ *valp = llval;
+ if (*valp != llval) {
+ errmsgno(EX_BAD,
+ "Value %lld is too large for data type 'long'.\n",
+ llval);
+ ret = -1;
+ }
+ return (ret);
+}
+
+int
+getllnum(char *arg, Llong *lvalp)
+{
+ int ret = 1;
+
+ *lvalp = number(arg, &ret);
+ return (ret);
+}
diff --git a/wodim/iso9660.h b/wodim/iso9660.h
new file mode 100644
index 0000000..efcb212
--- /dev/null
+++ b/wodim/iso9660.h
@@ -0,0 +1,180 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)iso9660.h 1.5 04/03/02 Copyright 1996, 2004 J. Schilling */
+/*
+ * Copyright (c) 1996, 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define _delta(from, to) ((to) - (from) + 1)
+
+#define VD_BOOT 0
+#define VD_PRIMARY 1
+#define VD_SUPPLEMENT 2
+#define VD_PARTITION 3
+#define VD_TERM 255
+
+#define VD_ID "CD001"
+
+struct iso9660_voldesc {
+ char vd_type [_delta(1, 1)];
+ char vd_id [_delta(2, 6)];
+ char vd_version [_delta(7, 7)];
+ char vd_fill [_delta(8, 2048)];
+};
+
+struct iso9660_boot_voldesc {
+ char vd_type [_delta(1, 1)];
+ char vd_id [_delta(2, 6)];
+ char vd_version [_delta(7, 7)];
+ char vd_bootsys [_delta(8, 39)];
+ char vd_bootid [_delta(40, 71)];
+ char vd_bootcode [_delta(72, 2048)];
+};
+
+struct iso9660_pr_voldesc {
+ char vd_type [_delta(1, 1)];
+ char vd_id [_delta(2, 6)];
+ char vd_version [_delta(7, 7)];
+ char vd_unused1 [_delta(8, 8)];
+ char vd_system_id [_delta(9, 40)];
+ char vd_volume_id [_delta(41, 72)];
+ char vd_unused2 [_delta(73, 80)];
+ char vd_volume_space_size [_delta(81, 88)];
+ char vd_unused3 [_delta(89, 120)];
+ char vd_volume_set_size [_delta(121, 124)];
+ char vd_volume_seq_number [_delta(125, 128)];
+ char vd_lbsize [_delta(129, 132)];
+ char vd_path_table_size [_delta(133, 140)];
+ char vd_pos_path_table_l [_delta(141, 144)];
+ char vd_opt_pos_path_table_l [_delta(145, 148)];
+ char vd_pos_path_table_m [_delta(149, 152)];
+ char vd_opt_pos_path_table_m [_delta(153, 156)];
+ char vd_root_dir [_delta(157, 190)];
+ char vd_volume_set_id [_delta(191, 318)];
+ char vd_publisher_id [_delta(319, 446)];
+ char vd_data_preparer_id [_delta(447, 574)];
+ char vd_application_id [_delta(575, 702)];
+ char vd_copyr_file_id [_delta(703, 739)];
+ char vd_abstr_file_id [_delta(740, 776)];
+ char vd_bibl_file_id [_delta(777, 813)];
+ char vd_create_time [_delta(814, 830)];
+ char vd_mod_time [_delta(831, 847)];
+ char vd_expiry_time [_delta(848, 864)];
+ char vd_effective_time [_delta(865, 881)];
+ char vd_file_struct_vers [_delta(882, 882)];
+ char vd_reserved1 [_delta(883, 883)];
+ char vd_application_use [_delta(884, 1395)];
+ char vd_fill [_delta(1396, 2048)];
+};
+
+struct iso9660_dir {
+ char dr_len [_delta(1, 1)];
+ char dr_eattr_len [_delta(2, 2)];
+ char dr_eattr_pos [_delta(3, 10)];
+ char dr_data_len [_delta(11, 18)];
+ char dr_recording_time [_delta(19, 25)];
+ char dr_file_flags [_delta(26, 26)];
+ char dr_file_unit_size [_delta(27, 27)];
+ char dr_interleave_gap [_delta(28, 28)];
+ char dr_volume_seq_number [_delta(29, 32)];
+ char dr_file_name_len [_delta(33, 33)];
+ char dr_file_name [_delta(34, 34)];
+};
+
+struct iso9660_dtime {
+ unsigned char dt_year;
+ unsigned char dt_month;
+ unsigned char dt_day;
+ unsigned char dt_hour;
+ unsigned char dt_minute;
+ unsigned char dt_second;
+ char dt_gmtoff;
+};
+
+struct iso9660_ltime {
+ char lt_year [_delta(1, 4)];
+ char lt_month [_delta(5, 6)];
+ char lt_day [_delta(7, 8)];
+ char lt_hour [_delta(9, 10)];
+ char lt_minute [_delta(11, 12)];
+ char lt_second [_delta(13, 14)];
+ char lt_hsecond [_delta(15, 16)];
+ char lt_gmtoff [_delta(17, 17)];
+};
+
+struct iso9660_path_table {
+ char pt_di_len [_delta(1, 1)];
+ char pt_eattr_len [_delta(2, 2)];
+ char pt_eattr_pos [_delta(3, 6)];
+ char pt_di_parent [_delta(7, 8)];
+ char pt_name [_delta(9, 9)];
+};
+
+struct iso9660_eattr {
+ char ea_owner [_delta(1, 4)];
+ char ea_group [_delta(5, 8)];
+ char ea_perm [_delta(9, 10)];
+ char ea_ctime [_delta(11, 27)];
+ char ea_mtime [_delta(28, 44)];
+ char ea_extime [_delta(45, 61)];
+ char ea_eftime [_delta(62, 78)];
+ char ea_record_format [_delta(79, 79)];
+ char ea_record_attr [_delta(80, 80)];
+ char ea_record_len [_delta(81, 84)];
+ char ea_system_id [_delta(85, 116)];
+ char ea_system_use [_delta(117, 180)];
+ char ea_version [_delta(181, 181)];
+ char ea_esc_seq_len [_delta(182, 182)];
+ char ea_reserved1 [_delta(183, 246)];
+ char ea_appl_use_len [_delta(247, 250)];
+ char ea_appl_use [_delta(251, 251)]; /* actually more */
+/* char ea_esc_seq [_delta(xxx, xxx)]; */
+
+};
+
+#define PERM_MB_ONE 0xAAAA
+
+#define PERM_RSYS 0x0001
+#define PERM_XSYS 0x0004
+
+#define PERM_RUSR 0x0010
+#define PERM_XUSR 0x0040
+
+#define PERM_RGRP 0x0100
+#define PERM_XGRP 0x0400
+
+#define PERM_ROTH 0x1000
+#define PERM_XOTH 0x4000
+
+
+#define GET_UBYTE(a) a_to_u_byte(a)
+#define GET_SBYTE(a) a_to_byte(a)
+#define GET_SHORT(a) a_to_u_2_byte(&((unsigned char *) (a))[0])
+#define GET_BSHORT(a) a_to_u_2_byte(&((unsigned char *) (a))[2])
+#define GET_INT(a) a_to_4_byte(&((unsigned char *) (a))[0])
+#define GET_LINT(a) la_to_4_byte(&((unsigned char *) (a))[0])
+#define GET_BINT(a) a_to_4_byte(&((unsigned char *) (a))[4])
diff --git a/wodim/isosize.c b/wodim/isosize.c
new file mode 100644
index 0000000..b3f6624
--- /dev/null
+++ b/wodim/isosize.c
@@ -0,0 +1,86 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)isosize.c 1.9 04/03/02 Copyright 1996, 2001-2004 J. Schilling */
+/*
+ * Copyright (c) 1996, 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <statdefs.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <utypes.h>
+#include <intcvt.h>
+
+#include "iso9660.h"
+#include "wodim.h" /* to verify isosize() prototype */
+
+Llong isosize(int f);
+
+Llong
+isosize(int f)
+{
+ struct iso9660_voldesc vd;
+ struct iso9660_pr_voldesc *vp;
+ Llong isize;
+ struct stat sb;
+ mode_t mode;
+
+ /*
+ * First check if a bad guy tries to call isosize()
+ * with an unappropriate file descriptor.
+ * return -1 in this case.
+ */
+ if (isatty(f))
+ return ((Llong)-1);
+ if (fstat(f, &sb) < 0)
+ return ((Llong)-1);
+ mode = sb.st_mode & S_IFMT;
+ if (!S_ISREG(mode) && !S_ISBLK(mode) && !S_ISCHR(mode))
+ return ((Llong)-1);
+
+ if (lseek(f, (off_t)(16L * 2048L), SEEK_SET) == -1)
+ return ((Llong)-1);
+
+ vp = (struct iso9660_pr_voldesc *) &vd;
+
+ do {
+ read(f, &vd, sizeof (vd)); /* FIXME: check return value */
+ if (GET_UBYTE(vd.vd_type) == VD_PRIMARY)
+ break;
+
+ } while (GET_UBYTE(vd.vd_type) != VD_TERM);
+
+ lseek(f, (off_t)0L, SEEK_SET);
+
+ if (GET_UBYTE(vd.vd_type) != VD_PRIMARY)
+ return (-1L);
+
+ isize = (Llong)GET_BINT(vp->vd_volume_space_size);
+ isize *= GET_BSHORT(vp->vd_lbsize);
+ return (isize);
+}
diff --git a/wodim/misc.c b/wodim/misc.c
new file mode 100644
index 0000000..ada60a4
--- /dev/null
+++ b/wodim/misc.c
@@ -0,0 +1,82 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)misc.c 1.4 04/03/02 Copyright 1998, 2001-2004 J. Schilling */
+/*
+ * Misc support functions
+ *
+ * Copyright (c) 1998, 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <timedefs.h>
+#include <stdio.h>
+#include <standard.h>
+#include <schily.h>
+
+void timevaldiff(struct timeval *start, struct timeval *stop);
+void prtimediff(const char *fmt, struct timeval *start, struct timeval *stop);
+
+void
+timevaldiff(struct timeval *start, struct timeval *stop)
+{
+ struct timeval tv;
+
+ tv.tv_sec = stop->tv_sec - start->tv_sec;
+ tv.tv_usec = stop->tv_usec - start->tv_usec;
+ while (tv.tv_usec > 1000000) {
+ tv.tv_usec -= 1000000;
+ tv.tv_sec += 1;
+ }
+ while (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
+ }
+ *stop = tv;
+}
+
+void
+prtimediff(const char *fmt, struct timeval *start, struct timeval *stop)
+{
+ struct timeval tv;
+
+ tv.tv_sec = stop->tv_sec - start->tv_sec;
+ tv.tv_usec = stop->tv_usec - start->tv_usec;
+ while (tv.tv_usec > 1000000) {
+ tv.tv_usec -= 1000000;
+ tv.tv_sec += 1;
+ }
+ while (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec -= 1;
+ }
+ /*
+ * We need to cast timeval->* to long because
+ * of the broken sys/time.h in Linux.
+ */
+ printf("%s%4ld.%03lds\n", fmt, (long)tv.tv_sec, (long)tv.tv_usec/1000);
+ flush();
+}
diff --git a/wodim/mmcvendor.h b/wodim/mmcvendor.h
new file mode 100644
index 0000000..3c71afc
--- /dev/null
+++ b/wodim/mmcvendor.h
@@ -0,0 +1,87 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)mmcvendor.h 1.3 04/03/01 Copyright 2002-2004 J. Schilling */
+/*
+ * Copyright (c) 2002-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MMCVENDOR_H
+#define _MMCVENDOR_H
+
+#include <utypes.h>
+#include <btorder.h>
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct ricoh_mode_page_30 {
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0xE = 14 Bytes */
+ Ucbit BUEFS :1; /* Burn-Free supported */
+ Ucbit TWBFS :1; /* Test Burn-Free sup. */
+ Ucbit res_2_23 :2;
+ Ucbit ARSCS :1; /* Auto read speed control supp. */
+ Ucbit AWSCS :1; /* Auto write speed control supp. */
+ Ucbit res_2_67 :2;
+ Ucbit BUEFE :1; /* Burn-Free enabled */
+ Ucbit res_2_13 :3;
+ Ucbit ARSCE :1; /* Auto read speed control enabled */
+ Ucbit AWSCD :1; /* Auto write speed control disabled */
+ Ucbit res_3_67 :2;
+ Uchar link_counter[2]; /* Burn-Free link counter */
+ Uchar res[10]; /* Padding up to 16 bytes */
+};
+
+#else /* Motorola bitorder */
+
+struct ricoh_mode_page_30 {
+ MP_P_CODE; /* parsave & pagecode */
+ Uchar p_len; /* 0xE = 14 Bytes */
+ Ucbit res_2_67 :2;
+ Ucbit AWSCS :1; /* Auto write speed control supp. */
+ Ucbit ARSCS :1; /* Auto read speed control supp. */
+ Ucbit res_2_23 :2;
+ Ucbit TWBFS :1; /* Test Burn-Free sup. */
+ Ucbit BUEFS :1; /* Burn-Free supported */
+ Ucbit res_3_67 :2;
+ Ucbit AWSCD :1; /* Auto write speed control disabled */
+ Ucbit ARSCE :1; /* Auto read speed control enabled */
+ Ucbit res_2_13 :3;
+ Ucbit BUEFE :1; /* Burn-Free enabled */
+ Uchar link_counter[2]; /* Burn-Free link counter */
+ Uchar res[10]; /* Padding up to 16 bytes */
+};
+#endif
+
+struct cd_mode_vendor {
+ struct scsi_mode_header header;
+ union cd_v_pagex {
+ struct ricoh_mode_page_30 page30;
+ } pagex;
+};
+
+
+#endif /* _MMCVENDOR_H */
diff --git a/wodim/modes.c b/wodim/modes.c
new file mode 100644
index 0000000..6718352
--- /dev/null
+++ b/wodim/modes.c
@@ -0,0 +1,289 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)modes.c 1.25 04/03/02 Copyright 1988, 1997-2001, 2004 J. Schilling */
+/*
+ * SCSI mode page handling
+ *
+ * Copyright (c) 1988, 1997-2001, 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <utypes.h>
+#include <standard.h>
+#include <schily.h>
+#include <usal/usalcmd.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+
+int scsi_compliant;
+
+static BOOL has_mode_page(SCSI *usalp, int page, char *pagename, int *lenp);
+BOOL get_mode_params(SCSI *usalp, int page, char *pagename, Uchar *modep,
+ Uchar *cmodep, Uchar *dmodep, Uchar *smodep, int *lenp);
+BOOL set_mode_params(SCSI *usalp, char *pagename, Uchar *modep, int len,
+ int save, int secsize);
+
+#define XXX
+
+#ifdef XXX
+static BOOL
+has_mode_page(SCSI *usalp, int page, char *pagename, int *lenp)
+{
+ Uchar mode[0x100];
+ int hdlen;
+ int len = 1; /* Nach SCSI Norm */
+ int try = 0;
+ struct scsi_mode_page_header *mp;
+
+ /*
+ * ATAPI drives (used e.g. by IOMEGA) from y2k have the worst firmware
+ * I've seen. They create DMA buffer overruns if we request less than
+ * 3 bytes with 6 byte mode sense which equals 4 byte with 10 byte mode
+ * sense. In order to prevent repeated bus resets, we remember this
+ * bug.
+ *
+ * IOMEGA claims that they are using Philips clone drives but a Philips
+ * drive I own does not have the problem.
+ */
+ if ((usalp->dflags & DRF_MODE_DMA_OVR) != 0)
+ len = sizeof (struct scsi_mode_header);
+again:
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+ if (lenp)
+ *lenp = 0;
+
+ usalp->silent++;
+ (void) unit_ready(usalp);
+/* Maxoptix bringt Aborted cmd 0x0B mit code 0x4E (overlapping cmds)*/
+
+ /*
+ * The Matsushita CW-7502 will sometimes deliver a zeroed
+ * mode page 2A if "Page n default" is used instead of "current".
+ */
+ if (mode_sense(usalp, mode, len, page, 0) < 0) { /* Page n current */
+ usalp->silent--;
+ if (len < (int)sizeof (struct scsi_mode_header) && try == 0) {
+ len = sizeof (struct scsi_mode_header);
+ goto again;
+ }
+ return (FALSE);
+ } else {
+ if (len > 1 && try == 0) {
+ /*
+ * If we come here, we got a hard failure with the
+ * fist try. Remember this (IOMEGA USB) firmware bug.
+ */
+ if ((usalp->dflags & DRF_MODE_DMA_OVR) == 0) {
+ /* XXX if (!nowarn) */
+ errmsgno(EX_BAD,
+ "Warning: controller creates hard SCSI failure when retrieving %s page.\n",
+ pagename);
+ usalp->dflags |= DRF_MODE_DMA_OVR;
+ }
+ }
+ len = ((struct scsi_mode_header *)mode)->sense_data_len + 1;
+ }
+ /*
+ * ATAPI drives as used by IOMEGA may receive a SCSI bus device reset
+ * in between these two mode sense commands.
+ */
+ (void) unit_ready(usalp);
+ if (mode_sense(usalp, mode, len, page, 0) < 0) { /* Page n current */
+ usalp->silent--;
+ return (FALSE);
+ }
+ usalp->silent--;
+
+ if (usalp->verbose)
+ usal_prbytes("Mode Sense Data", mode, len - usal_getresid(usalp));
+ hdlen = sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len;
+ mp = (struct scsi_mode_page_header *)(mode + hdlen);
+ if (usalp->verbose)
+ usal_prbytes("Mode Page Data", (Uchar *)mp, mp->p_len+2);
+
+ if (mp->p_len == 0) {
+ if (!scsi_compliant && try == 0) {
+ len = hdlen;
+ /*
+ * add sizeof page header (page # + len byte)
+ * (should normaly result in len == 14)
+ * this allowes to work with:
+ * Quantum Q210S (wants at least 13)
+ * MD2x (wants at least 4)
+ */
+ len += 2;
+ try++;
+ goto again;
+ }
+ /* XXX if (!nowarn) */
+ errmsgno(EX_BAD,
+ "Warning: controller returns zero sized %s page.\n",
+ pagename);
+ }
+ if (!scsi_compliant &&
+ (len < (int)(mp->p_len + hdlen + 2))) {
+ len = mp->p_len + hdlen + 2;
+
+ /* XXX if (!nowarn) */
+ errmsgno(EX_BAD,
+ "Warning: controller returns wrong size for %s page.\n",
+ pagename);
+ }
+ if (mp->p_code != page) {
+ /* XXX if (!nowarn) */
+ errmsgno(EX_BAD,
+ "Warning: controller returns wrong page %X for %s page (%X).\n",
+ mp->p_code, pagename, page);
+ return (FALSE);
+ }
+
+ if (lenp)
+ *lenp = len;
+ return (mp->p_len > 0);
+}
+#endif
+
+BOOL
+get_mode_params(SCSI *usalp, int page, char *pagename, Uchar *modep,
+ Uchar *cmodep, Uchar *dmodep, Uchar *smodep, int *lenp)
+{
+ int len;
+ BOOL ret = TRUE;
+
+#ifdef XXX
+ if (lenp)
+ *lenp = 0;
+ if (!has_mode_page(usalp, page, pagename, &len)) {
+ if (!usalp->silent) errmsgno(EX_BAD,
+ "Warning: controller does not support %s page.\n",
+ pagename);
+ return (FALSE);
+ }
+ if (lenp)
+ *lenp = len;
+#else
+ if (lenp == 0)
+ len = 0xFF;
+#endif
+
+ if (modep) {
+ fillbytes(modep, 0x100, '\0');
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (mode_sense(usalp, modep, len, page, 0) < 0) { /* Page x current */
+ errmsgno(EX_BAD, "Cannot get %s data.\n", pagename);
+ ret = FALSE;
+ } else if (usalp->verbose) {
+ usal_prbytes("Mode Sense Data", modep, len - usal_getresid(usalp));
+ }
+ }
+
+ if (cmodep) {
+ fillbytes(cmodep, 0x100, '\0');
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (mode_sense(usalp, cmodep, len, page, 1) < 0) { /* Page x change */
+ errmsgno(EX_BAD, "Cannot get %s mask.\n", pagename);
+ ret = FALSE;
+ } else if (usalp->verbose) {
+ usal_prbytes("Mode Sense Data", cmodep, len - usal_getresid(usalp));
+ }
+ }
+
+ if (dmodep) {
+ fillbytes(dmodep, 0x100, '\0');
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (mode_sense(usalp, dmodep, len, page, 2) < 0) { /* Page x default */
+ errmsgno(EX_BAD, "Cannot get default %s data.\n",
+ pagename);
+ ret = FALSE;
+ } else if (usalp->verbose) {
+ usal_prbytes("Mode Sense Data", dmodep, len - usal_getresid(usalp));
+ }
+ }
+
+ if (smodep) {
+ fillbytes(smodep, 0x100, '\0');
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (mode_sense(usalp, smodep, len, page, 3) < 0) { /* Page x saved */
+ errmsgno(EX_BAD, "Cannot get saved %s data.\n", pagename);
+ ret = FALSE;
+ } else if (usalp->verbose) {
+ usal_prbytes("Mode Sense Data", smodep, len - usal_getresid(usalp));
+ }
+ }
+
+ return (ret);
+}
+
+BOOL
+set_mode_params(SCSI *usalp, char *pagename, Uchar *modep, int len, int save,
+ int secsize)
+{
+ int i;
+
+ ((struct scsi_modesel_header *)modep)->sense_data_len = 0;
+ ((struct scsi_modesel_header *)modep)->res2 = 0;
+
+ i = ((struct scsi_mode_header *)modep)->blockdesc_len;
+ if (i > 0) {
+ i_to_3_byte(
+ ((struct scsi_mode_data *)modep)->blockdesc.nlblock,
+ 0);
+ if (secsize >= 0)
+ i_to_3_byte(((struct scsi_mode_data *)modep)->blockdesc.lblen,
+ secsize);
+ }
+
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (save == 0 || mode_select(usalp, modep, len, save, usalp->inq->data_format >= 2) < 0) {
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+ if (mode_select(usalp, modep, len, 0, usalp->inq->data_format >= 2) < 0) {
+ if (usalp->silent == 0) {
+ errmsgno(EX_BAD,
+ "Warning: using default %s data.\n",
+ pagename);
+ usal_prbytes("Mode Select Data", modep, len);
+ }
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
diff --git a/wodim/movesect.c b/wodim/movesect.c
new file mode 100644
index 0000000..ef4bc70
--- /dev/null
+++ b/wodim/movesect.c
@@ -0,0 +1,104 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)movesect.c 1.3 04/03/02 Copyright 2001, 2004 J. Schilling */
+/*
+ * Copyright (c) 2001, 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <standard.h>
+#include <utypes.h>
+#include <schily.h>
+
+#include "wodim.h"
+#include "movesect.h"
+
+void scatter_secs(track_t *trackp, char *bp, int nsecs);
+
+/*
+ * Scatter input sector size records over buffer to make them
+ * output sector size.
+ *
+ * If input sector size is less than output sector size,
+ *
+ * | sector_0 || sector_1 || ... || sector_n ||
+ *
+ * will be convterted into:
+ *
+ * | sector_0 |grass|| sector_1 |grass|| ... || sector_n |grass||
+ *
+ * Sector_n must me moved n * grass_size forward,
+ * Sector_1 must me moved 1 * grass_size forward
+ *
+ *
+ * If output sector size is less than input sector size,
+ *
+ * | sector_0 |grass|| sector_1 |grass|| ... || sector_n |grass||
+ *
+ * will be convterted into:
+ *
+ * | sector_0 || sector_1 || ... || sector_n ||
+ *
+ * Sector_1 must me moved 1 * grass_size backward,
+ * Sector_n must me moved n * grass_size backward,
+ *
+ * Sector_0 must never be moved.
+ */
+void
+scatter_secs(track_t *trackp, char *bp, int nsecs)
+{
+ char *from;
+ char *to;
+ int isecsize = trackp->isecsize;
+ int secsize = trackp->secsize;
+ int i;
+
+ if (secsize == isecsize)
+ return;
+
+ nsecs -= 1; /* we do not move sector # 0 */
+
+ if (secsize < isecsize) {
+ from = bp + isecsize;
+ to = bp + secsize;
+
+ for (i = nsecs; i > 0; i--) {
+ movebytes(from, to, secsize);
+ from += isecsize;
+ to += secsize;
+ }
+ } else {
+ from = bp + (nsecs * isecsize);
+ to = bp + (nsecs * secsize);
+
+ for (i = nsecs; i > 0; i--) {
+ movebytes(from, to, isecsize);
+ from -= isecsize;
+ to -= secsize;
+ }
+ }
+}
diff --git a/wodim/movesect.h b/wodim/movesect.h
new file mode 100644
index 0000000..64f09b7
--- /dev/null
+++ b/wodim/movesect.h
@@ -0,0 +1,45 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)movesect.h 1.1 01/06/02 Copyright 2001 J. Schilling */
+/*
+ * Copyright (c) 2001 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _MOVESECT_H
+#define _MOVESECT_H
+
+#define move2352(from, to) movebytes(from, to, 2352)
+#define move2336(from, to) movebytes(from, to, 2336)
+#define move2048(from, to) movebytes(from, to, 2048)
+
+#define fill2352(p, val) fillbytes(p, 2352, val)
+#define fill2048(p, val) fillbytes(p, 2048, val)
+#define fill96(p, val) fillbytes(p, 96, val)
+
+extern void scatter_secs(track_t *trackp, char *bp, int nsecs);
+
+#endif
diff --git a/wodim/scsi_cdr.c b/wodim/scsi_cdr.c
new file mode 100644
index 0000000..fbb8270
--- /dev/null
+++ b/wodim/scsi_cdr.c
@@ -0,0 +1,2918 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_cdr.c 1.137 04/05/25 Copyright 1995-2004 J. Schilling */
+/*
+ * SCSI command functions for cdrecord
+ * covering pre-MMC standard functions up to MMC-2
+ *
+ * Copyright (c) 1995-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * NOTICE: The Philips CDD 521 has several firmware bugs.
+ * One of them is not to respond to a SCSI selection
+ * within 200ms if the general load on the
+ * SCSI bus is high. To deal with this problem
+ * most of the SCSI commands are send with the
+ * SCG_CMD_RETRY flag enabled.
+ */
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsimmc.h"
+#include "wodim.h"
+#include "scsi_scan.h"
+
+#define strbeg(s1, s2) (strstr((s2), (s1)) == (s2))
+
+BOOL unit_ready(SCSI *usalp);
+BOOL wait_unit_ready(SCSI *usalp, int secs);
+BOOL scsi_in_progress(SCSI *usalp);
+BOOL cdr_underrun(SCSI *usalp);
+int test_unit_ready(SCSI *usalp);
+int rezero_unit(SCSI *usalp);
+int request_sense(SCSI *usalp);
+int request_sense_b(SCSI *usalp, caddr_t bp, int cnt);
+int inquiry(SCSI *usalp, caddr_t, int);
+int read_capacity(SCSI *usalp);
+void print_capacity(SCSI *usalp, FILE *f);
+int scsi_load_unload(SCSI *usalp, int);
+int scsi_prevent_removal(SCSI *usalp, int);
+int scsi_start_stop_unit(SCSI *usalp, int, int, BOOL immed);
+int scsi_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl);
+int scsi_get_speed(SCSI *usalp, int *readspeedp, int *writespeedp);
+int qic02(SCSI *usalp, int);
+int write_xscsi(SCSI *usalp, caddr_t, long, long, int);
+int write_xg0(SCSI *usalp, caddr_t, long, long, int);
+int write_xg1(SCSI *usalp, caddr_t, long, long, int);
+int write_xg5(SCSI *usalp, caddr_t, long, long, int);
+int seek_scsi(SCSI *usalp, long addr);
+int seek_g0(SCSI *usalp, long addr);
+int seek_g1(SCSI *usalp, long addr);
+int scsi_flush_cache(SCSI *usalp, BOOL immed);
+int read_buffer(SCSI *usalp, caddr_t bp, int cnt, int mode);
+int write_buffer(SCSI *usalp, char *buffer, long length, int mode,
+ int bufferid, long offset);
+int read_subchannel(SCSI *usalp, caddr_t bp, int track, int cnt, int msf,
+ int subq, int fmt);
+int read_toc(SCSI *usalp, caddr_t, int, int, int, int);
+int read_toc_philips(SCSI *usalp, caddr_t, int, int, int, int);
+int read_header(SCSI *usalp, caddr_t, long, int, int);
+int read_disk_info(SCSI *usalp, caddr_t, int);
+int read_track_info(SCSI *usalp, caddr_t, int type, int addr, int cnt);
+int read_rzone_info(SCSI *usalp, caddr_t bp, int cnt);
+int reserve_tr_rzone(SCSI *usalp, long size);
+int read_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int addr, int layer,
+ int fmt);
+int send_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int layer, int fmt);
+int send_opc(SCSI *usalp, caddr_t, int cnt, int doopc);
+int read_track_info_philips(SCSI *usalp, caddr_t, int, int);
+int scsi_close_tr_session(SCSI *usalp, int type, int track, BOOL immed);
+int read_master_cue(SCSI *usalp, caddr_t bp, int sheet, int cnt);
+int send_cue_sheet(SCSI *usalp, caddr_t bp, long size);
+int read_buff_cap(SCSI *usalp, long *, long *);
+int scsi_blank(SCSI *usalp, long addr, int blanktype, BOOL immed);
+int scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background);
+int scsi_set_streaming(SCSI *usalp, caddr_t addr, int size);
+BOOL allow_atapi(SCSI *usalp, BOOL new);
+int mode_select(SCSI *usalp, Uchar *, int, int, int);
+int mode_sense(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+int mode_select_sg0(SCSI *usalp, Uchar *, int, int, int);
+int mode_sense_sg0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+int mode_select_g0(SCSI *usalp, Uchar *, int, int, int);
+int mode_select_g1(SCSI *usalp, Uchar *, int, int, int);
+int mode_sense_g0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+int mode_sense_g1(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+int read_tochdr(SCSI *usalp, cdr_t *, int *, int *);
+int read_cdtext(SCSI *usalp);
+int read_trackinfo(SCSI *usalp, int, long *, struct msf *, int *, int *,
+ int *);
+int read_B0(SCSI *usalp, BOOL isbcd, long *b0p, long *lop);
+int read_session_offset(SCSI *usalp, long *);
+int read_session_offset_philips(SCSI *usalp, long *);
+int sense_secsize(SCSI *usalp, int current);
+int select_secsize(SCSI *usalp, int);
+BOOL is_cddrive(SCSI *usalp);
+BOOL is_unknown_dev(SCSI *usalp);
+int read_scsi(SCSI *usalp, caddr_t, long, int);
+int read_g0(SCSI *usalp, caddr_t, long, int);
+int read_g1(SCSI *usalp, caddr_t, long, int);
+BOOL getdev(SCSI *usalp, BOOL);
+void printinq(SCSI *usalp, FILE *f);
+void printdev(SCSI *usalp);
+BOOL do_inquiry(SCSI *usalp, BOOL);
+BOOL recovery_needed(SCSI *usalp, cdr_t *);
+int scsi_load(SCSI *usalp, cdr_t *);
+int scsi_unload(SCSI *usalp, cdr_t *);
+int scsi_cdr_write(SCSI *usalp, caddr_t bp, long sectaddr, long size,
+ int blocks, BOOL islast);
+struct cd_mode_page_2A * mmc_cap(SCSI *usalp, Uchar *modep);
+void mmc_getval(struct cd_mode_page_2A *mp, BOOL *cdrrp, BOOL *cdwrp,
+ BOOL *cdrrwp, BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp);
+BOOL is_mmc(SCSI *usalp, BOOL *cdwp, BOOL *dvdwp);
+BOOL mmc_check(SCSI *usalp, BOOL *cdrrp, BOOL *cdwrp, BOOL *cdrrwp,
+ BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp);
+static void print_speed(char *fmt, int val);
+void print_capabilities(SCSI *usalp);
+
+BOOL
+unit_ready(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (test_unit_ready(usalp) >= 0) /* alles OK */
+ return (TRUE);
+ else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */
+ return (FALSE);
+
+ if (usal_sense_key(usalp) == SC_UNIT_ATTENTION) {
+ if (test_unit_ready(usalp) >= 0) /* alles OK */
+ return (TRUE);
+ }
+ if ((usal_cmd_status(usalp) & ST_BUSY) != 0) {
+ /*
+ * Busy/reservation_conflict
+ */
+ usleep(500000);
+ if (test_unit_ready(usalp) >= 0) /* alles OK */
+ return (TRUE);
+ }
+ if (usal_sense_key(usalp) == -1) { /* non extended Sense */
+ if (usal_sense_code(usalp) == 4) /* NOT_READY */
+ return (FALSE);
+ return (TRUE);
+ }
+ /* FALSE wenn NOT_READY */
+ return (usal_sense_key(usalp) != SC_NOT_READY);
+}
+
+BOOL
+wait_unit_ready(SCSI *usalp, int secs)
+{
+ int i;
+ int c;
+ int k;
+ int ret;
+
+ usalp->silent++;
+ ret = test_unit_ready(usalp); /* eat up unit attention */
+ if (ret < 0)
+ ret = test_unit_ready(usalp); /* got power on condition? */
+ usalp->silent--;
+
+ if (ret >= 0) /* success that's enough */
+ return (TRUE);
+
+ usalp->silent++;
+ for (i = 0; i < secs && (ret = test_unit_ready(usalp)) < 0; i++) {
+ if (usalp->scmd->scb.busy != 0) {
+ sleep(1);
+ continue;
+ }
+ c = usal_sense_code(usalp);
+ k = usal_sense_key(usalp);
+ /*
+ * Abort quickly if it does not make sense to wait.
+ * 0x30 == Cannot read medium
+ * 0x3A == Medium not present
+ */
+ if ((k == SC_NOT_READY && (c == 0x3A || c == 0x30)) ||
+ (k == SC_MEDIUM_ERROR)) {
+ if (usalp->silent <= 1)
+ usal_printerr(usalp);
+ usalp->silent--;
+ return (FALSE);
+ }
+ sleep(1);
+ }
+ usalp->silent--;
+ if (ret < 0)
+ return (FALSE);
+ return (TRUE);
+}
+
+BOOL
+scsi_in_progress(SCSI *usalp)
+{
+ if (usal_sense_key(usalp) == SC_NOT_READY &&
+ /*
+ * Logigal unit not ready operation/long_write in progress
+ */
+ usal_sense_code(usalp) == 0x04 &&
+ (usal_sense_qual(usalp) == 0x04 || /* CyberDr. "format in progress"*/
+ usal_sense_qual(usalp) == 0x07 || /* "operation in progress" */
+ usal_sense_qual(usalp) == 0x08)) { /* "long write in progress" */
+ return (TRUE);
+ } else {
+ if (usalp->silent <= 1)
+ usal_printerr(usalp);
+ }
+ return (FALSE);
+}
+
+BOOL
+cdr_underrun(SCSI *usalp)
+{
+ if ((usal_sense_key(usalp) != SC_ILLEGAL_REQUEST &&
+ usal_sense_key(usalp) != SC_MEDIUM_ERROR))
+ return (FALSE);
+
+ if ((usal_sense_code(usalp) == 0x21 &&
+ (usal_sense_qual(usalp) == 0x00 || /* logical block address out of range */
+ usal_sense_qual(usalp) == 0x02)) || /* invalid address for write */
+
+ (usal_sense_code(usalp) == 0x0C &&
+ usal_sense_qual(usalp) == 0x09)) { /* write error - loss of streaming */
+ return (TRUE);
+ }
+ /*
+ * XXX Bei manchen Brennern kommt mach dem der Brennvorgang bereits
+ * XXX eine Weile gelaufen ist ein 5/24/0 Invalid field in CDB.
+ * XXX Daher sollte man testen ob schon geschrieben wurde...
+ */
+ return (FALSE);
+}
+
+int
+test_unit_ready(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA | (usalp->silent ? SCG_SILENT:0);
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_TEST_UNIT_READY;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "test unit ready";
+
+ return (usal_cmd(usalp));
+}
+
+int
+rezero_unit(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_REZERO_UNIT;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+
+ usalp->cmdname = "rezero unit";
+
+ return (usal_cmd(usalp));
+}
+
+int
+request_sense(SCSI *usalp)
+{
+ char sensebuf[CCS_SENSE_LEN];
+ register struct usal_cmd *scmd = usalp->scmd;
+
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = sensebuf;
+ scmd->size = sizeof (sensebuf);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_REQUEST_SENSE;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = CCS_SENSE_LEN;
+
+ usalp->cmdname = "request_sense";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ usal_prsense((Uchar *)sensebuf, CCS_SENSE_LEN - usal_getresid(usalp));
+ return (0);
+}
+
+int
+request_sense_b(SCSI *usalp, caddr_t bp, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_REQUEST_SENSE;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = cnt;
+
+ usalp->cmdname = "request_sense";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+inquiry(SCSI *usalp, caddr_t bp, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes(bp, cnt, '\0');
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_INQUIRY;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = cnt;
+
+ usalp->cmdname = "inquiry";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ if (usalp->verbose)
+ usal_prbytes("Inquiry Data :", (Uchar *)bp, cnt - usal_getresid(usalp));
+ return (0);
+}
+
+int
+read_capacity(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)usalp->cap;
+ scmd->size = sizeof (struct scsi_capacity);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x25; /* Read Capacity */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, 0); /* Full Media */
+
+ usalp->cmdname = "read capacity";
+
+ if (usal_cmd(usalp) < 0) {
+ return (-1);
+ } else {
+ long cbsize;
+ long cbaddr;
+
+ /*
+ * c_bsize & c_baddr are signed Int32_t
+ * so we use signed int conversion here.
+ */
+ cbsize = a_to_4_byte(&usalp->cap->c_bsize);
+ cbaddr = a_to_4_byte(&usalp->cap->c_baddr);
+ usalp->cap->c_bsize = cbsize;
+ usalp->cap->c_baddr = cbaddr;
+ }
+ return (0);
+}
+
+void
+print_capacity(SCSI *usalp, FILE *f)
+{
+ long kb;
+ long mb;
+ long prmb;
+ double dkb;
+
+ dkb = (usalp->cap->c_baddr+1.0) * (usalp->cap->c_bsize/1024.0);
+ kb = dkb;
+ mb = dkb / 1024.0;
+ prmb = dkb / 1000.0 * 1.024;
+ fprintf(f, "Capacity: %ld Blocks = %ld kBytes = %ld MBytes = %ld prMB\n",
+ (long)usalp->cap->c_baddr+1, kb, mb, prmb);
+ fprintf(f, "Sectorsize: %ld Bytes\n", (long)usalp->cap->c_bsize);
+}
+
+int
+scsi_load_unload(SCSI *usalp, int load)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xA6;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.addr[1] = load?3:2;
+ scmd->cdb.g5_cdb.count[2] = 0; /* slot # */
+
+ usalp->cmdname = "medium load/unload";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+scsi_prevent_removal(SCSI *usalp, int prevent)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = 0x1E;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = prevent & 1;
+
+ usalp->cmdname = "prevent/allow medium removal";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+
+int
+scsi_start_stop_unit(SCSI *usalp, int flg, int loej, BOOL immed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = 0x1B; /* Start Stop Unit */
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.count = (flg ? 1:0) | (loej ? 2:0);
+
+ if (immed)
+ scmd->cdb.cmd_cdb[1] |= 0x01;
+
+ usalp->cmdname = "start/stop unit";
+
+ return (usal_cmd(usalp));
+}
+
+int
+scsi_set_streaming(SCSI *usalp, caddr_t perf_desc, int size)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = perf_desc;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xB6;
+ scmd->cdb.cmd_cdb[11] = 0;
+ scmd->cdb.cmd_cdb[10] = size;
+
+ usalp->cmdname = "set streaming";
+
+ if(usalp->verbose)
+ fprintf(stderr, "scsi_set_streaming\n");
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+scsi_set_speed(SCSI *usalp, int readspeed, int writespeed, int rotctl)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xBB;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+
+ if (readspeed < 0)
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], 0xFFFF);
+ else
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], readspeed);
+ if (writespeed < 0)
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xFFFF);
+ else
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], writespeed);
+
+ scmd->cdb.cmd_cdb[1] |= rotctl & 0x03;
+
+ usalp->cmdname = "set cd speed";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+scsi_get_speed(SCSI *usalp, int *readspeedp, int *writespeedp)
+{
+ struct cd_mode_page_2A *mp;
+ Uchar m[256];
+ int val;
+
+ usalp->silent++;
+ mp = mmc_cap(usalp, m); /* Get MMC capabilities in allocated mp */
+ usalp->silent--;
+ if (mp == NULL)
+ return (-1); /* Pre SCSI-3/mmc drive */
+
+ val = a_to_u_2_byte(mp->cur_read_speed);
+ if (readspeedp)
+ *readspeedp = val;
+
+ if (mp->p_len >= 28)
+ val = a_to_u_2_byte(mp->v3_cur_write_speed);
+ else
+ val = a_to_u_2_byte(mp->cur_write_speed);
+ if (writespeedp)
+ *writespeedp = val;
+
+ return (0);
+}
+
+
+int
+qic02(SCSI *usalp, int cmd)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = DEF_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = 0x0D; /* qic02 Sysgen SC4000 */
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.mid_addr = cmd;
+
+ usalp->cmdname = "qic 02";
+ return (usal_cmd(usalp));
+}
+
+#define G0_MAXADDR 0x1FFFFFL
+
+int
+write_xscsi(SCSI *usalp, caddr_t bp, long addr, long size, int cnt)
+{
+ if (addr <= G0_MAXADDR)
+ return (write_xg0(usalp, bp, addr, size, cnt));
+ else
+ return (write_xg1(usalp, bp, addr, size, cnt));
+}
+
+int
+write_xg0(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long addr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int cnt /* sectorcount */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+/* scmd->flags = SCG_DISRE_ENA;*/
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_WRITE;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, addr);
+ scmd->cdb.g0_cdb.count = cnt;
+
+ usalp->cmdname = "write_g0";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (size - usal_getresid(usalp));
+}
+
+int
+write_xg1(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long addr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int cnt /* sectorcount */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+/* scmd->flags = SCG_DISRE_ENA;*/
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = SC_EWRITE;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "write_g1";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (size - usal_getresid(usalp));
+}
+
+int
+write_xg5(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long addr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int cnt /* sectorcount */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+/* scmd->flags = SCG_DISRE_ENA;*/
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g5_cdb.cmd = 0xAA;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ g5_cdbaddr(&scmd->cdb.g5_cdb, addr);
+ g5_cdblen(&scmd->cdb.g5_cdb, cnt);
+
+ usalp->cmdname = "write_g5";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (size - usal_getresid(usalp));
+}
+
+int
+seek_scsi(SCSI *usalp, long addr)
+{
+ if (addr <= G0_MAXADDR)
+ return (seek_g0(usalp, addr));
+ else
+ return (seek_g1(usalp, addr));
+}
+
+int
+seek_g0(SCSI *usalp, long addr)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = 0x0B; /* Seek */
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, addr);
+
+ usalp->cmdname = "seek_g0";
+
+ return (usal_cmd(usalp));
+}
+
+int
+seek_g1(SCSI *usalp, long addr)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x2B; /* Seek G1 */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+
+ usalp->cmdname = "seek_g1";
+
+ return (usal_cmd(usalp));
+}
+
+int
+scsi_flush_cache(SCSI *usalp, BOOL immed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 2 * 60; /* Max: sizeof (CDR-cache)/150KB/s */
+ scmd->cdb.g1_cdb.cmd = 0x35;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ if (immed)
+ scmd->cdb.cmd_cdb[1] |= 0x02;
+
+ usalp->cmdname = "flush cache";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_buffer(SCSI *usalp, caddr_t bp, int cnt, int mode)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->dma_read = 1;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x3C; /* Read Buffer */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.cmd_cdb[1] |= (mode & 7);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read buffer";
+
+ return (usal_cmd(usalp));
+}
+
+int
+write_buffer(SCSI *usalp, char *buffer, long length, int mode, int bufferid,
+ long offset)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ char *cdb;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = buffer;
+ scmd->size = length;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+
+ cdb = (char *)scmd->cdb.cmd_cdb;
+
+ cdb[0] = 0x3B;
+ cdb[1] = mode & 7;
+ cdb[2] = bufferid;
+ cdb[3] = offset >> 16;
+ cdb[4] = (offset >> 8) & 0xff;
+ cdb[5] = offset & 0xff;
+ cdb[6] = length >> 16;
+ cdb[7] = (length >> 8) & 0xff;
+ cdb[8] = length & 0xff;
+
+ usalp->cmdname = "write_buffer";
+
+ if (usal_cmd(usalp) >= 0)
+ return (1);
+ return (0);
+}
+
+int
+read_subchannel(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int subq,
+ int fmt)
+
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x42;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ if (msf)
+ scmd->cdb.g1_cdb.res = 1;
+ if (subq)
+ scmd->cdb.g1_cdb.addr[0] = 0x40;
+ scmd->cdb.g1_cdb.addr[1] = fmt;
+ scmd->cdb.g1_cdb.res6 = track;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read subchannel";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_toc(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int fmt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x43;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ if (msf)
+ scmd->cdb.g1_cdb.res = 1;
+ scmd->cdb.g1_cdb.addr[0] = fmt & 0x0F;
+ scmd->cdb.g1_cdb.res6 = track;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read toc";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_toc_philips(SCSI *usalp, caddr_t bp, int track, int cnt, int msf, int fmt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 4 * 60; /* May last 174s on a TEAC CD-R55S */
+ scmd->cdb.g1_cdb.cmd = 0x43;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ if (msf)
+ scmd->cdb.g1_cdb.res = 1;
+ scmd->cdb.g1_cdb.res6 = track;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ if (fmt & 1)
+ scmd->cdb.g1_cdb.vu_96 = 1;
+ if (fmt & 2)
+ scmd->cdb.g1_cdb.vu_97 = 1;
+
+ usalp->cmdname = "read toc";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_header(SCSI *usalp, caddr_t bp, long addr, int cnt, int msf)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x44;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ if (msf)
+ scmd->cdb.g1_cdb.res = 1;
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read header";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_disk_info(SCSI *usalp, caddr_t bp, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 4 * 60; /* Needs up to 2 minutes */
+ scmd->cdb.g1_cdb.cmd = 0x51;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read disk info";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_track_info(SCSI *usalp, caddr_t bp, int type, int addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 4 * 60; /* Needs up to 2 minutes */
+ scmd->cdb.g1_cdb.cmd = 0x52;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+/* scmd->cdb.cmd_cdb[1] = type & 0x03;*/
+ scmd->cdb.cmd_cdb[1] = type;
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr); /* LBA/Track/Session */
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read track info";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+reserve_track(SCSI *usalp, Ulong size)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x53;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], size);
+
+ usalp->cmdname = "reserve track";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ return (0);
+
+}
+
+int
+read_rzone_info(SCSI *usalp, caddr_t bp, int cnt)
+{
+ return (read_track_info(usalp, bp, TI_TYPE_LBA, 0, cnt));
+}
+
+int
+reserve_tr_rzone(SCSI *usalp, long size /* number of blocks */)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x53;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+
+ i_to_4_byte(&scmd->cdb.g1_cdb.addr[3], size);
+
+ usalp->cmdname = "reserve_track_rzone";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int addr, int layer,
+ int fmt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */
+ scmd->cdb.g5_cdb.cmd = 0xAD;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ g5_cdbaddr(&scmd->cdb.g5_cdb, addr);
+ g5_cdblen(&scmd->cdb.g5_cdb, cnt);
+ scmd->cdb.g5_cdb.count[0] = layer;
+ scmd->cdb.g5_cdb.count[1] = fmt;
+
+ usalp->cmdname = "read dvd structure";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+send_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int layer, int fmt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */
+ scmd->cdb.g5_cdb.cmd = 0xBF;
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ g5_cdblen(&scmd->cdb.g5_cdb, cnt);
+
+ scmd->cdb.cmd_cdb[7] = fmt;
+
+ usalp->cmdname = "send dvd structure";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+send_opc(SCSI *usalp, caddr_t bp, int cnt, int doopc)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 60;
+ scmd->cdb.g1_cdb.cmd = 0x54;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.reladr = doopc?1:0;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "send opc";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_track_info_philips(SCSI *usalp, caddr_t bp, int track, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xE5;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, track);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read track info";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+scsi_close_tr_session(SCSI *usalp, int type, int track, BOOL immed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 8 * 60; /* Needs up to 4 minutes */
+ scmd->cdb.g1_cdb.cmd = 0x5B;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = type;
+ scmd->cdb.g1_cdb.addr[3] = track;
+
+ if (immed)
+ scmd->cdb.g1_cdb.reladr = 1;
+/* scmd->cdb.cmd_cdb[1] |= 0x01;*/
+#ifdef nono
+ scmd->cdb.g1_cdb.reladr = 1; /* IMM hack to test Mitsumi behaviour*/
+#endif
+
+ usalp->cmdname = "close track/session";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+read_master_cue(SCSI *usalp, caddr_t bp, int sheet, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x59; /* Read master cue */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[2] = sheet;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read master cue";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (0);
+}
+
+int
+send_cue_sheet(SCSI *usalp, caddr_t bp, long size)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x5D; /* Send CUE sheet */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, size);
+
+ usalp->cmdname = "send_cue_sheet";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ return (size - scmd->resid);
+}
+
+int
+read_buff_cap(SCSI *usalp, long *sp, long *fp)
+{
+ char resp[12];
+ Ulong freespace;
+ Ulong bufsize;
+ int per;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)resp;
+ scmd->size = sizeof (resp);
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x5C; /* Read buffer cap */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdblen(&scmd->cdb.g1_cdb, sizeof (resp));
+
+ usalp->cmdname = "read buffer cap";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+
+ bufsize = a_to_u_4_byte(&resp[4]);
+ freespace = a_to_u_4_byte(&resp[8]);
+ if (sp)
+ *sp = bufsize;
+ if (fp)
+ *fp = freespace;
+
+ if (usalp->verbose || (sp == 0 && fp == 0))
+ printf("BFree: %ld K BSize: %ld K\n", freespace >> 10, bufsize >> 10);
+
+ if (bufsize == 0)
+ return (0);
+ per = (100 * (bufsize - freespace)) / bufsize;
+ if (per < 0)
+ return (0);
+ if (per > 100)
+ return (100);
+ return (per);
+}
+
+int
+scsi_blank(SCSI *usalp, long addr, int blanktype, BOOL immed)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 160 * 60; /* full blank at 1x could take 80 minutes */
+ scmd->cdb.g5_cdb.cmd = 0xA1; /* Blank */
+ scmd->cdb.g0_cdb.high_addr = blanktype;
+ g1_cdbaddr(&scmd->cdb.g5_cdb, addr);
+
+ if (immed)
+ scmd->cdb.g5_cdb.res |= 8;
+/* scmd->cdb.cmd_cdb[1] |= 0x10;*/
+
+ usalp->cmdname = "blank unit";
+
+ return (usal_cmd(usalp));
+}
+
+int
+scsi_format(SCSI *usalp, caddr_t addr, int size, BOOL background)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+ int progress=0, ret=-1, pid=-1;
+ unsigned char sense_table[18];
+ int i;
+
+ printf("scsi_format: preparing\n");
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = addr;
+ scmd->size = size;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G5_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->timeout = 160 * 60; /* Do not know what to set */
+ scmd->cdb.g5_cdb.cmd = 0x04; /* Format Unit */
+ scmd->cdb.cmd_cdb[1] = 0x11; /* "FmtData" and "Format Code" */
+ scmd->cdb.cmd_cdb[5] = 0;
+
+ usalp->cmdname = "format unit";
+
+ printf("scsi_format: running\n");
+ ret = (usal_cmd(usalp));
+ printf("scsi_format: post processing %d\n", ret);
+ if (ret == -1) return ret;
+ if (background) {
+ if ((pid=fork()) == (pid_t)-1)
+ perror ("- [unable to fork()]");
+ else {
+ if (!pid) {
+ while (1) {
+ if (test_unit_ready(usalp) >= 0)
+ break;
+ sleep(1);
+ }
+ return ret;
+ }
+ }
+ }
+ printf("Formating in progress: 0.00 %% done.");
+ sleep(20);
+ i = 0;
+ while (progress < 0xfff0 && !(progress == 0 && i > 50)) {
+ test_unit_ready(usalp);
+ request_sense_b(usalp, (caddr_t)sense_table, 18);
+ progress = sense_table[16]<<8|sense_table[17];
+ printf("\rFormating in progress: %.2f %% done [%d]. ", (float)(progress*100)/0x10000,progress);
+ usleep(100000);
+ i++;
+ /*for (i=0; i < 18; i++) {
+ printf("%d ", sense_table[i]);
+ }*/
+ }
+ sleep(10);
+ printf("\rFormating in progress: 100.00 %% done. \n");
+ if (pid) exit (0);
+ return ret;
+}
+
+/*
+ * XXX First try to handle ATAPI:
+ * XXX ATAPI cannot handle SCSI 6 byte commands.
+ * XXX We try to simulate 6 byte mode sense/select.
+ */
+static BOOL is_atapi;
+
+BOOL
+allow_atapi(SCSI *usalp, BOOL new)
+{
+ BOOL old = is_atapi;
+ Uchar mode[256];
+
+ if (new == old)
+ return (old);
+
+ usalp->silent++;
+ /*
+ * If a bad drive has been reset before, we may need to fire up two
+ * test unit ready commands to clear status.
+ */
+ (void) unit_ready(usalp);
+ if (new &&
+ mode_sense_g1(usalp, mode, 8, 0x3F, 0) < 0) { /* All pages current */
+ new = FALSE;
+ }
+ usalp->silent--;
+
+ is_atapi = new;
+ return (old);
+}
+
+int
+mode_select(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf)
+{
+ if (is_atapi)
+ return (mode_select_sg0(usalp, dp, cnt, smp, pf));
+ return (mode_select_g0(usalp, dp, cnt, smp, pf));
+}
+
+int
+mode_sense(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf)
+{
+ if (is_atapi)
+ return (mode_sense_sg0(usalp, dp, cnt, page, pcf));
+ return (mode_sense_g0(usalp, dp, cnt, page, pcf));
+}
+
+/*
+ * Simulate mode select g0 with mode select g1.
+ */
+int
+mode_select_sg0(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf)
+{
+ Uchar xmode[256+4];
+ int amt = cnt;
+
+ if (amt < 1 || amt > 255) {
+ /* XXX clear SCSI error codes ??? */
+ return (-1);
+ }
+
+ if (amt < 4) { /* Data length. medium type & VU */
+ amt += 1;
+ } else {
+ amt += 4;
+ movebytes(&dp[4], &xmode[8], cnt-4);
+ }
+ xmode[0] = 0;
+ xmode[1] = 0;
+ xmode[2] = dp[1];
+ xmode[3] = dp[2];
+ xmode[4] = 0;
+ xmode[5] = 0;
+ i_to_2_byte(&xmode[6], (unsigned int)dp[3]);
+
+ if (usalp->verbose) usal_prbytes("Mode Parameters (un-converted)", dp, cnt);
+
+ return (mode_select_g1(usalp, xmode, amt, smp, pf));
+}
+
+/*
+ * Simulate mode sense g0 with mode sense g1.
+ */
+int
+mode_sense_sg0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf)
+{
+ Uchar xmode[256+4];
+ int amt = cnt;
+ int len;
+
+ if (amt < 1 || amt > 255) {
+ /* XXX clear SCSI error codes ??? */
+ return (-1);
+ }
+
+ fillbytes((caddr_t)xmode, sizeof (xmode), '\0');
+ if (amt < 4) { /* Data length. medium type & VU */
+ amt += 1;
+ } else {
+ amt += 4;
+ }
+ if (mode_sense_g1(usalp, xmode, amt, page, pcf) < 0)
+ return (-1);
+
+ amt = cnt - usal_getresid(usalp);
+/*
+ * For tests: Solaris 8 & LG CD-ROM always returns resid == amt
+ */
+/* amt = cnt;*/
+ if (amt > 4)
+ movebytes(&xmode[8], &dp[4], amt-4);
+ len = a_to_u_2_byte(xmode);
+ if (len == 0) {
+ dp[0] = 0;
+ } else if (len < 6) {
+ if (len > 2)
+ len = 2;
+ dp[0] = len;
+ } else {
+ dp[0] = len - 3;
+ }
+ dp[1] = xmode[2];
+ dp[2] = xmode[3];
+ len = a_to_u_2_byte(&xmode[6]);
+ dp[3] = len;
+
+ if (usalp->verbose) usal_prbytes("Mode Sense Data (converted)", dp, amt);
+ return (0);
+}
+
+int
+mode_select_g0(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)dp;
+ scmd->size = cnt;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_MODE_SELECT;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
+ scmd->cdb.g0_cdb.count = cnt;
+
+ if (usalp->verbose) {
+ fprintf(stderr, "%s ", smp?"Save":"Set ");
+ usal_prbytes("Mode Parameters", dp, cnt);
+ }
+
+ usalp->cmdname = "mode select g0";
+
+ return (usal_cmd(usalp));
+}
+
+int
+mode_select_g1(SCSI *usalp, Uchar *dp, int cnt, int smp, int pf)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)dp;
+ scmd->size = cnt;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x55;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ if (usalp->verbose) {
+ printf("%s ", smp?"Save":"Set ");
+ usal_prbytes("Mode Parameters", dp, cnt);
+ }
+
+ usalp->cmdname = "mode select g1";
+
+ return (usal_cmd(usalp));
+}
+
+int
+mode_sense_g0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)dp;
+ scmd->size = 0xFF;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_MODE_SENSE;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+#ifdef nonono
+ scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */
+#endif
+ scmd->cdb.g0_cdb.mid_addr = (page&0x3F) | ((pcf<<6)&0xC0);
+ scmd->cdb.g0_cdb.count = page ? 0xFF : 24;
+ scmd->cdb.g0_cdb.count = cnt;
+
+ usalp->cmdname = "mode sense g0";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ if (usalp->verbose) usal_prbytes("Mode Sense Data", dp, cnt - usal_getresid(usalp));
+ return (0);
+}
+
+int
+mode_sense_g1(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = (caddr_t)dp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x5A;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+#ifdef nonono
+ scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */
+#endif
+ scmd->cdb.g1_cdb.addr[0] = (page&0x3F) | ((pcf<<6)&0xC0);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "mode sense g1";
+
+ if (usal_cmd(usalp) < 0)
+ return (-1);
+ if (usalp->verbose) usal_prbytes("Mode Sense Data", dp, cnt - usal_getresid(usalp));
+ return (0);
+}
+
+struct trackdesc {
+ Uchar res0;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+ Ucbit control : 4;
+ Ucbit adr : 4;
+#else /* Motorola byteorder */
+ Ucbit adr : 4;
+ Ucbit control : 4;
+#endif
+
+ Uchar track;
+ Uchar res3;
+ Uchar addr[4];
+};
+
+struct diskinfo {
+ struct tocheader hd;
+ struct trackdesc desc[1];
+};
+
+struct siheader {
+ Uchar len[2];
+ Uchar finished;
+ Uchar unfinished;
+};
+
+struct sidesc {
+ Uchar sess_number;
+ Uchar res1;
+ Uchar track;
+ Uchar res3;
+ Uchar addr[4];
+};
+
+struct sinfo {
+ struct siheader hd;
+ struct sidesc desc[1];
+};
+
+struct trackheader {
+ Uchar mode;
+ Uchar res[3];
+ Uchar addr[4];
+};
+#define TRM_ZERO 0
+#define TRM_USER_ECC 1 /* 2048 bytes user data + 288 Bytes ECC/EDC */
+#define TRM_USER 2 /* All user data (2336 bytes) */
+
+
+int
+read_tochdr(SCSI *usalp, cdr_t *dp, int *fp, int *lp)
+{
+ struct tocheader *tp;
+ char xb[256];
+ int len;
+
+ tp = (struct tocheader *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_TOC) < 0) {
+ if (usalp->silent == 0)
+ errmsgno(EX_BAD, "Cannot read TOC header\n");
+ return (-1);
+ }
+ len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2;
+ if (len >= 4) {
+ if (fp)
+ *fp = tp->first;
+ if (lp)
+ *lp = tp->last;
+ return (0);
+ }
+ return (-1);
+}
+
+int
+read_cdtext(SCSI *usalp)
+{
+ struct tocheader *tp;
+ char xb[256];
+ int len;
+ char xxb[10000];
+
+ tp = (struct tocheader *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc(usalp, xb, 0, sizeof (struct tocheader), 0, FMT_CDTEXT) < 0) {
+ if (usalp->silent == 0 || usalp->verbose > 0)
+ errmsgno(EX_BAD, "Cannot read CD-Text header\n");
+ return (-1);
+ }
+ len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2;
+ printf("CD-Text len: %d\n", len);
+
+ if (read_toc(usalp, xxb, 0, len, 0, FMT_CDTEXT) < 0) {
+ if (usalp->silent == 0)
+ errmsgno(EX_BAD, "Cannot read CD-Text\n");
+ return (-1);
+ }
+ {
+ FILE *f = fileopen("cdtext.dat", "wctb");
+ filewrite(f, xxb, len);
+ }
+ return (0);
+}
+
+int
+read_trackinfo(SCSI *usalp, int track, long *offp, struct msf *msfp, int *adrp,
+ int *controlp, int *modep)
+{
+ struct diskinfo *dp;
+ char xb[256];
+ int len;
+
+ dp = (struct diskinfo *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC) < 0) {
+ if (usalp->silent <= 0)
+ errmsgno(EX_BAD, "Cannot read TOC\n");
+ return (-1);
+ }
+ len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2;
+ if (len < (int)sizeof (struct diskinfo))
+ return (-1);
+
+ if (offp)
+ *offp = a_to_4_byte(dp->desc[0].addr);
+ if (adrp)
+ *adrp = dp->desc[0].adr;
+ if (controlp)
+ *controlp = dp->desc[0].control;
+
+ if (msfp) {
+ usalp->silent++;
+ if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 1, FMT_TOC)
+ >= 0) {
+ msfp->msf_min = dp->desc[0].addr[1];
+ msfp->msf_sec = dp->desc[0].addr[2];
+ msfp->msf_frame = dp->desc[0].addr[3];
+ } else if (read_toc(usalp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC)
+ >= 0) {
+ /*
+ * Some drives (e.g. the Philips CDD-522) don't support
+ * to read the TOC in MSF mode.
+ */
+ long off = a_to_4_byte(dp->desc[0].addr);
+
+ lba_to_msf(off, msfp);
+ } else {
+ msfp->msf_min = 0;
+ msfp->msf_sec = 0;
+ msfp->msf_frame = 0;
+ }
+ usalp->silent--;
+ }
+
+ if (modep == NULL)
+ return (0);
+
+ if (track == 0xAA) {
+ *modep = -1;
+ return (0);
+ }
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+
+ usalp->silent++;
+ if (read_header(usalp, xb, *offp, 8, 0) >= 0) {
+ *modep = xb[0];
+ } else if (read_track_info_philips(usalp, xb, track, 14) >= 0) {
+ *modep = xb[0xb] & 0xF;
+ } else {
+ *modep = -1;
+ }
+ usalp->silent--;
+ return (0);
+}
+
+int
+read_B0(SCSI *usalp, BOOL isbcd, long *b0p, long *lop)
+{
+ struct fdiskinfo *dp;
+ struct ftrackdesc *tp;
+ char xb[8192];
+ char *pe;
+ int len;
+ long l;
+
+ dp = (struct fdiskinfo *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc_philips(usalp, xb, 1, sizeof (struct tocheader), 0, FMT_FULLTOC) < 0) {
+ return (-1);
+ }
+ len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2;
+ if (len < (int)sizeof (struct fdiskinfo))
+ return (-1);
+ if (read_toc_philips(usalp, xb, 1, len, 0, FMT_FULLTOC) < 0) {
+ return (-1);
+ }
+ if (usalp->verbose) {
+ usal_prbytes("TOC data: ", (Uchar *)xb,
+ len > (int)sizeof (xb) - usal_getresid(usalp) ?
+ sizeof (xb) - usal_getresid(usalp) : len);
+
+ tp = &dp->desc[0];
+ pe = &xb[len];
+
+ while ((char *)tp < pe) {
+ usal_prbytes("ENT: ", (Uchar *)tp, 11);
+ tp++;
+ }
+ }
+ tp = &dp->desc[0];
+ pe = &xb[len];
+
+ for (; (char *)tp < pe; tp++) {
+ if (tp->sess_number != dp->hd.last)
+ continue;
+ if (tp->point != 0xB0)
+ continue;
+ if (usalp->verbose)
+ usal_prbytes("B0: ", (Uchar *)tp, 11);
+ if (isbcd) {
+ l = msf_to_lba(from_bcd(tp->amin),
+ from_bcd(tp->asec),
+ from_bcd(tp->aframe), TRUE);
+ } else {
+ l = msf_to_lba(tp->amin,
+ tp->asec,
+ tp->aframe, TRUE);
+ }
+ if (b0p)
+ *b0p = l;
+
+ if (usalp->verbose)
+ printf("B0 start: %ld\n", l);
+
+ if (isbcd) {
+ l = msf_to_lba(from_bcd(tp->pmin),
+ from_bcd(tp->psec),
+ from_bcd(tp->pframe), TRUE);
+ } else {
+ l = msf_to_lba(tp->pmin,
+ tp->psec,
+ tp->pframe, TRUE);
+ }
+
+ if (usalp->verbose)
+ printf("B0 lout: %ld\n", l);
+ if (lop)
+ *lop = l;
+ return (0);
+ }
+ return (-1);
+}
+
+
+/*
+ * Return address of first track in last session (SCSI-3/mmc version).
+ */
+int
+read_session_offset(SCSI *usalp, long *offp)
+{
+ struct diskinfo *dp;
+ char xb[256];
+ int len;
+
+ dp = (struct diskinfo *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc(usalp, (caddr_t)xb, 0, sizeof (struct tocheader), 0, FMT_SINFO) < 0)
+ return (-1);
+
+ if (usalp->verbose)
+ usal_prbytes("tocheader: ",
+ (Uchar *)xb, sizeof (struct tocheader) - usal_getresid(usalp));
+
+ len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2;
+ if (len > (int)sizeof (xb)) {
+ errmsgno(EX_BAD, "Session info too big.\n");
+ return (-1);
+ }
+ if (read_toc(usalp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0)
+ return (-1);
+
+ if (usalp->verbose)
+ usal_prbytes("tocheader: ",
+ (Uchar *)xb, len - usal_getresid(usalp));
+
+ dp = (struct diskinfo *)xb;
+ if (offp)
+ *offp = a_to_u_4_byte(dp->desc[0].addr);
+ return (0);
+}
+
+/*
+ * Return address of first track in last session (pre SCSI-3 version).
+ */
+int
+read_session_offset_philips(SCSI *usalp, long *offp)
+{
+ struct sinfo *sp;
+ char xb[256];
+ int len;
+
+ sp = (struct sinfo *)xb;
+
+ fillbytes((caddr_t)xb, sizeof (xb), '\0');
+ if (read_toc_philips(usalp, (caddr_t)xb, 0, sizeof (struct siheader), 0, FMT_SINFO) < 0)
+ return (-1);
+ len = a_to_u_2_byte(sp->hd.len) + sizeof (struct siheader)-2;
+ if (len > (int)sizeof (xb)) {
+ errmsgno(EX_BAD, "Session info too big.\n");
+ return (-1);
+ }
+ if (read_toc_philips(usalp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0)
+ return (-1);
+ /*
+ * Old drives return the number of finished sessions in first/finished
+ * a descriptor is returned for each session.
+ * New drives return the number of the first and last session
+ * one descriptor for the last finished session is returned
+ * as in SCSI-3
+ * In all cases the lowest session number is set to 1.
+ */
+ sp = (struct sinfo *)xb;
+ if (offp)
+ *offp = a_to_u_4_byte(sp->desc[sp->hd.finished-1].addr);
+ return (0);
+}
+
+int
+sense_secsize(SCSI *usalp, int current)
+{
+ Uchar mode[0x100];
+ Uchar *p;
+ Uchar *ep;
+ int len;
+ int secsize = -1;
+
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ usalp->silent--;
+
+ /* XXX Quick and dirty, musz verallgemeinert werden !!! */
+
+ fillbytes(mode, sizeof (mode), '\0');
+ usalp->silent++;
+
+ len = sizeof (struct scsi_mode_header) +
+ sizeof (struct scsi_mode_blockdesc);
+ /*
+ * Wenn wir hier get_mode_params() nehmen bekommen wir die Warnung:
+ * Warning: controller returns wrong page 1 for All pages page (3F).
+ */
+ if (mode_sense(usalp, mode, len, 0x3F, current?0:2) < 0) {
+ fillbytes(mode, sizeof (mode), '\0');
+ if (mode_sense(usalp, mode, len, 0, current?0:2) < 0) { /* VU (block desc) */
+ usalp->silent--;
+ return (-1);
+ }
+ }
+ if (mode[3] == 8) {
+ if (usalp->debug) {
+ printf("Density: 0x%X\n", mode[4]);
+ printf("Blocks: %ld\n", a_to_u_3_byte(&mode[5]));
+ printf("Blocklen:%ld\n", a_to_u_3_byte(&mode[9]));
+ }
+ secsize = a_to_u_3_byte(&mode[9]);
+ }
+ fillbytes(mode, sizeof (mode), '\0');
+ /*
+ * The ACARD TECH AEC-7720 ATAPI<->SCSI adaptor
+ * chokes if we try to transfer more than 0x40 bytes with
+ * mode_sense of all pages. So try to avoid to run this
+ * command if possible.
+ */
+ if (usalp->debug &&
+ mode_sense(usalp, mode, 0xFE, 0x3F, current?0:2) >= 0) { /* All Pages */
+
+ ep = mode+mode[0]; /* Points to last byte of data */
+ p = &mode[4];
+ p += mode[3];
+ printf("Pages: ");
+ while (p < ep) {
+ printf("0x%X ", *p&0x3F);
+ p += p[1]+2;
+ }
+ printf("\n");
+ }
+ usalp->silent--;
+
+ return (secsize);
+}
+
+int
+select_secsize(SCSI *usalp, int secsize)
+{
+ struct scsi_mode_data md;
+ int count = sizeof (struct scsi_mode_header) +
+ sizeof (struct scsi_mode_blockdesc);
+
+ (void) test_unit_ready(usalp); /* clear any error situation */
+
+ fillbytes((caddr_t)&md, sizeof (md), '\0');
+ md.header.blockdesc_len = 8;
+ i_to_3_byte(md.blockdesc.lblen, secsize);
+
+ return (mode_select(usalp, (Uchar *)&md, count, 0, usalp->inq->data_format >= 2));
+}
+
+BOOL
+is_cddrive(SCSI *usalp)
+{
+ return (usalp->inq->type == INQ_ROMD || usalp->inq->type == INQ_WORM);
+}
+
+BOOL
+is_unknown_dev(SCSI *usalp)
+{
+ return (usalp->dev == DEV_UNKNOWN);
+}
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+#ifdef DEBUG
+
+int
+read_scsi(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ if (addr <= G0_MAXADDR && cnt < 256 && !is_atapi)
+ return (read_g0(usalp, bp, addr, cnt));
+ else
+ return (read_g1(usalp, bp, addr, cnt));
+}
+
+int
+read_g0(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (usalp->cap->c_bsize <= 0)
+ raisecond("capacity_not_set", 0L);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*usalp->cap->c_bsize;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G0_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g0_cdb.cmd = SC_READ;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+ g0_cdbaddr(&scmd->cdb.g0_cdb, addr);
+ scmd->cdb.g0_cdb.count = cnt;
+/* scmd->cdb.g0_cdb.vu_56 = 1;*/
+
+ usalp->cmdname = "read_g0";
+
+ return (usal_cmd(usalp));
+}
+
+int
+read_g1(SCSI *usalp, caddr_t bp, long addr, int cnt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (usalp->cap->c_bsize <= 0)
+ raisecond("capacity_not_set", 0L);
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt*usalp->cap->c_bsize;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = SC_EREAD;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, addr);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "read_g1";
+
+ return (usal_cmd(usalp));
+}
+#endif /* DEBUG */
+
+BOOL
+getdev(SCSI *usalp, BOOL print)
+{
+ BOOL got_inquiry = TRUE;
+ char vendor_info[8+1];
+ char prod_ident[16+1];
+ char prod_revision[4+1];
+ int inq_len = 0;
+ register struct usal_cmd *scmd = usalp->scmd;
+ register struct scsi_inquiry *inq = usalp->inq;
+
+
+ fillbytes((caddr_t)inq, sizeof (*inq), '\0');
+ usalp->dev = DEV_UNKNOWN;
+ usalp->silent++;
+ (void) unit_ready(usalp);
+ if (scmd->error >= SCG_FATAL &&
+ !(scmd->scb.chk && scmd->sense_count > 0)) {
+ usalp->silent--;
+ return (FALSE);
+ }
+
+
+/* if (scmd->error < SCG_FATAL || scmd->scb.chk && scmd->sense_count > 0){*/
+
+ if (inquiry(usalp, (caddr_t)inq, sizeof (*inq)) < 0) {
+ got_inquiry = FALSE;
+ } else {
+ inq_len = sizeof (*inq) - usal_getresid(usalp);
+ }
+ if (!got_inquiry) {
+ if (usalp->verbose) {
+ printf(
+ "error: %d scb.chk: %d sense_count: %d sense.code: 0x%x\n",
+ scmd->error, scmd->scb.chk,
+ scmd->sense_count, scmd->sense.code);
+ }
+ /*
+ * Folgende Kontroller kennen das Kommando
+ * INQUIRY nicht:
+ *
+ * ADAPTEC ACB-4000, ACB-4010, ACB 4070
+ * SYSGEN SC4000
+ *
+ * Leider reagieren ACB40X0 und ACB5500 identisch
+ * wenn drive not ready (code == not ready),
+ * sie sind dann nicht zu unterscheiden.
+ */
+
+ if (scmd->scb.chk && scmd->sense_count == 4) {
+ /* Test auf SYSGEN */
+ (void) qic02(usalp, 0x12); /* soft lock on */
+ if (qic02(usalp, 1) < 0) { /* soft lock off */
+ usalp->dev = DEV_ACB40X0;
+/* usalp->dev = acbdev();*/
+ } else {
+ usalp->dev = DEV_SC4000;
+ inq->type = INQ_SEQD;
+ inq->removable = 1;
+ }
+ }
+ } else if (usalp->verbose) {
+ int i;
+ int len = inq->add_len + 5;
+ Uchar ibuf[256+5];
+ Uchar *ip = (Uchar *)inq;
+ Uchar c;
+
+ if (len > (int)sizeof (*inq) &&
+ inquiry(usalp, (caddr_t)ibuf, inq->add_len+5) >= 0) {
+ len = inq->add_len+5 - usal_getresid(usalp);
+ ip = ibuf;
+ } else {
+ len = sizeof (*inq);
+ }
+ printf("Inquiry Data : ");
+ for (i = 0; i < len; i++) {
+ c = ip[i];
+ if (c >= ' ' && c < 0177)
+ printf("%c", c);
+ else
+ printf(".");
+ }
+ printf("\n");
+ }
+
+ strncpy(vendor_info, inq->vendor_info, sizeof (inq->vendor_info));
+ strncpy(prod_ident, inq->prod_ident, sizeof (inq->prod_ident));
+ strncpy(prod_revision, inq->prod_revision, sizeof (inq->prod_revision));
+
+ vendor_info[sizeof (inq->vendor_info)] = '\0';
+ prod_ident[sizeof (inq->prod_ident)] = '\0';
+ prod_revision[sizeof (inq->prod_revision)] = '\0';
+
+ switch (inq->type) {
+
+ case INQ_DASD:
+ if (inq->add_len == 0 && inq->vendor_info[0] != '\0') {
+ Uchar *p;
+ /*
+ * NT-4.0 creates fake inquiry data for IDE disks.
+ * Unfortunately, it does not set add_len wo we
+ * check if vendor_info, prod_ident and prod_revision
+ * contains valid chars for a CCS inquiry.
+ */
+ if (inq_len >= 36)
+ inq->add_len = 31;
+
+ for (p = (Uchar *)&inq->vendor_info[0];
+ p < (Uchar *)&inq->prod_revision[4];
+ p++) {
+ if (*p < 0x20 || *p > 0x7E) {
+ inq->add_len = 0;
+ break;
+ }
+ }
+ }
+ if (inq->add_len == 0) {
+ if (usalp->dev == DEV_UNKNOWN && got_inquiry) {
+ usalp->dev = DEV_ACB5500;
+ strcpy(inq->vendor_info,
+ "ADAPTEC ACB-5500 FAKE");
+
+ } else switch (usalp->dev) {
+
+ case DEV_ACB40X0:
+ strcpy(inq->vendor_info,
+ "ADAPTEC ACB-40X0 FAKE");
+ break;
+ case DEV_ACB4000:
+ strcpy(inq->vendor_info,
+ "ADAPTEC ACB-4000 FAKE");
+ break;
+ case DEV_ACB4010:
+ strcpy(inq->vendor_info,
+ "ADAPTEC ACB-4010 FAKE");
+ break;
+ case DEV_ACB4070:
+ strcpy(inq->vendor_info,
+ "ADAPTEC ACB-4070 FAKE");
+ break;
+ }
+ } else if (inq->add_len < 31) {
+ usalp->dev = DEV_NON_CCS_DSK;
+
+ } else if (strbeg("EMULEX", vendor_info)) {
+ if (strbeg("MD21", prod_ident))
+ usalp->dev = DEV_MD21;
+ if (strbeg("MD23", prod_ident))
+ usalp->dev = DEV_MD23;
+ else
+ usalp->dev = DEV_CCS_GENDISK;
+ } else if (strbeg("ADAPTEC", vendor_info)) {
+ if (strbeg("ACB-4520", prod_ident))
+ usalp->dev = DEV_ACB4520A;
+ if (strbeg("ACB-4525", prod_ident))
+ usalp->dev = DEV_ACB4525;
+ else
+ usalp->dev = DEV_CCS_GENDISK;
+ } else if (strbeg("SONY", vendor_info) &&
+ strbeg("SMO-C501", prod_ident)) {
+ usalp->dev = DEV_SONY_SMO;
+ } else {
+ usalp->dev = DEV_CCS_GENDISK;
+ }
+ break;
+
+ case INQ_SEQD:
+ if (usalp->dev == DEV_SC4000) {
+ strcpy(inq->vendor_info,
+ "SYSGEN SC4000 FAKE");
+ } else if (inq->add_len == 0 &&
+ inq->removable &&
+ inq->ansi_version == 1) {
+ usalp->dev = DEV_MT02;
+ strcpy(inq->vendor_info,
+ "EMULEX MT02 FAKE");
+ }
+ break;
+
+/* case INQ_OPTD:*/
+ case INQ_ROMD:
+ case INQ_WORM:
+ if (strbeg("RXT-800S", prod_ident))
+ usalp->dev = DEV_RXT800S;
+
+ /*
+ * Start of CD-Recorders:
+ */
+ if (strbeg("ACER", vendor_info)) {
+ if (strbeg("CR-4020C", prod_ident))
+ usalp->dev = DEV_RICOH_RO_1420C;
+
+ } else if (strbeg("CREATIVE", vendor_info)) {
+ if (strbeg("CDR2000", prod_ident))
+ usalp->dev = DEV_RICOH_RO_1060C;
+
+ } else if (strbeg("GRUNDIG", vendor_info)) {
+ if (strbeg("CDR100IPW", prod_ident))
+ usalp->dev = DEV_CDD_2000;
+
+ } else if (strbeg("JVC", vendor_info)) {
+ if (strbeg("XR-W2001", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ else if (strbeg("XR-W2010", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ else if (strbeg("R2626", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+
+ } else if (strbeg("MITSBISH", vendor_info)) {
+
+#ifdef XXXX_REALLY
+ /* It's MMC compliant */
+ if (strbeg("CDRW226", prod_ident))
+ usalp->dev = DEV_MMC_CDRW;
+#else
+ /* EMPTY */
+#endif
+
+ } else if (strbeg("MITSUMI", vendor_info)) {
+ /* Don't know any product string */
+ usalp->dev = DEV_CDD_522;
+
+ } else if (strbeg("OPTIMA", vendor_info)) {
+ if (strbeg("CD-R 650", prod_ident))
+ usalp->dev = DEV_SONY_CDU_924;
+
+ } else if (strbeg("PHILIPS", vendor_info) ||
+ strbeg("IMS", vendor_info) ||
+ strbeg("KODAK", vendor_info) ||
+ strbeg("HP", vendor_info)) {
+
+ if (strbeg("CDD521/00", prod_ident))
+ usalp->dev = DEV_CDD_521_OLD;
+ else if (strbeg("CDD521/02", prod_ident))
+ usalp->dev = DEV_CDD_521_OLD; /* PCD 200R? */
+ else if (strbeg("CDD521", prod_ident))
+ usalp->dev = DEV_CDD_521;
+
+ if (strbeg("CDD522", prod_ident))
+ usalp->dev = DEV_CDD_522;
+ if (strbeg("PCD225", prod_ident))
+ usalp->dev = DEV_CDD_522;
+ if (strbeg("KHSW/OB", prod_ident)) /* PCD600 */
+ usalp->dev = DEV_PCD_600;
+ if (strbeg("CDR-240", prod_ident))
+ usalp->dev = DEV_CDD_2000;
+
+ if (strbeg("CDD20", prod_ident))
+ usalp->dev = DEV_CDD_2000;
+ if (strbeg("CDD26", prod_ident))
+ usalp->dev = DEV_CDD_2600;
+
+ if (strbeg("C4324/C4325", prod_ident))
+ usalp->dev = DEV_CDD_2000;
+ if (strbeg("CD-Writer 6020", prod_ident))
+ usalp->dev = DEV_CDD_2600;
+
+ } else if (strbeg("PINNACLE", vendor_info)) {
+ if (strbeg("RCD-1000", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ if (strbeg("RCD5020", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ if (strbeg("RCD5040", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ if (strbeg("RCD 4X4", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+
+ } else if (strbeg("PIONEER", vendor_info)) {
+ if (strbeg("CD-WO DW-S114X", prod_ident))
+ usalp->dev = DEV_PIONEER_DW_S114X;
+ else if (strbeg("CD-WO DR-R504X", prod_ident)) /* Reoprt from philip@merge.com */
+ usalp->dev = DEV_PIONEER_DW_S114X;
+ else if (strbeg("DVD-R DVR-S101", prod_ident))
+ usalp->dev = DEV_PIONEER_DVDR_S101;
+
+ } else if (strbeg("PLASMON", vendor_info)) {
+ if (strbeg("RF4100", prod_ident))
+ usalp->dev = DEV_PLASMON_RF_4100;
+ else if (strbeg("CDR4220", prod_ident))
+ usalp->dev = DEV_CDD_2000;
+
+ } else if (strbeg("PLEXTOR", vendor_info)) {
+ if (strbeg("CD-R PX-R24CS", prod_ident))
+ usalp->dev = DEV_RICOH_RO_1420C;
+
+ } else if (strbeg("RICOH", vendor_info)) {
+ if (strbeg("RO-1420C", prod_ident))
+ usalp->dev = DEV_RICOH_RO_1420C;
+ if (strbeg("RO1060C", prod_ident))
+ usalp->dev = DEV_RICOH_RO_1060C;
+
+ } else if (strbeg("SAF", vendor_info)) { /* Smart & Friendly */
+ if (strbeg("CD-R2004", prod_ident) ||
+ strbeg("CD-R2006 ", prod_ident))
+ usalp->dev = DEV_SONY_CDU_924;
+ else if (strbeg("CD-R2006PLUS", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ else if (strbeg("CD-RW226", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+ else if (strbeg("CD-R4012", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+
+ } else if (strbeg("SANYO", vendor_info)) {
+ if (strbeg("CD-WO CRD-R24S", prod_ident))
+ usalp->dev = DEV_CDD_521;
+
+ } else if (strbeg("SONY", vendor_info)) {
+ if (strbeg("CD-R CDU92", prod_ident) ||
+ strbeg("CD-R CDU94", prod_ident))
+ usalp->dev = DEV_SONY_CDU_924;
+
+ } else if (strbeg("TEAC", vendor_info)) {
+ if (strbeg("CD-R50S", prod_ident) ||
+ strbeg("CD-R55S", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+
+ } else if (strbeg("TRAXDATA", vendor_info) ||
+ strbeg("Traxdata", vendor_info)) {
+ if (strbeg("CDR4120", prod_ident))
+ usalp->dev = DEV_TEAC_CD_R50S;
+
+ } else if (strbeg("T.YUDEN", vendor_info)) {
+ if (strbeg("CD-WO EW-50", prod_ident))
+ usalp->dev = DEV_TYUDEN_EW50;
+
+ } else if (strbeg("WPI", vendor_info)) { /* Wearnes */
+ if (strbeg("CDR-632P", prod_ident))
+ usalp->dev = DEV_CDD_2600;
+
+ } else if (strbeg("YAMAHA", vendor_info)) {
+ if (strbeg("CDR10", prod_ident))
+ usalp->dev = DEV_YAMAHA_CDR_100;
+ if (strbeg("CDR200", prod_ident))
+ usalp->dev = DEV_YAMAHA_CDR_400;
+ if (strbeg("CDR400", prod_ident))
+ usalp->dev = DEV_YAMAHA_CDR_400;
+
+ } else if (strbeg("MATSHITA", vendor_info)) {
+ if (strbeg("CD-R CW-7501", prod_ident))
+ usalp->dev = DEV_MATSUSHITA_7501;
+ if (strbeg("CD-R CW-7502", prod_ident))
+ usalp->dev = DEV_MATSUSHITA_7502;
+ }
+ if (usalp->dev == DEV_UNKNOWN) {
+ /*
+ * We do not have Manufacturer strings for
+ * the following drives.
+ */
+ if (strbeg("CDS615E", prod_ident)) /* Olympus */
+ usalp->dev = DEV_SONY_CDU_924;
+ }
+ if (usalp->dev == DEV_UNKNOWN && inq->type == INQ_ROMD) {
+ BOOL cdrr = FALSE;
+ BOOL cdwr = FALSE;
+ BOOL cdrrw = FALSE;
+ BOOL cdwrw = FALSE;
+ BOOL dvd = FALSE;
+ BOOL dvdwr = FALSE;
+
+ usalp->dev = DEV_CDROM;
+
+ if (mmc_check(usalp, &cdrr, &cdwr, &cdrrw, &cdwrw,
+ &dvd, &dvdwr))
+ usalp->dev = DEV_MMC_CDROM;
+ if (cdwr)
+ usalp->dev = DEV_MMC_CDR;
+ if (cdwrw)
+ usalp->dev = DEV_MMC_CDRW;
+ if (dvd)
+ usalp->dev = DEV_MMC_DVD;
+ if (dvdwr)
+ usalp->dev = DEV_MMC_DVD_WR;
+ }
+ break;
+
+ case INQ_PROCD:
+ if (strbeg("BERTHOLD", vendor_info)) {
+ if (strbeg("", prod_ident))
+ usalp->dev = DEV_HRSCAN;
+ }
+ break;
+
+ case INQ_SCAN:
+ usalp->dev = DEV_MS300A;
+ break;
+ }
+ usalp->silent--;
+ if (!print)
+ return (TRUE);
+
+ if (usalp->dev == DEV_UNKNOWN && !got_inquiry) {
+#ifdef PRINT_INQ_ERR
+ usal_printerr(usalp);
+#endif
+ return (FALSE);
+ }
+
+ printinq(usalp, stdout);
+ return (TRUE);
+}
+
+void
+printinq(SCSI *usalp, FILE *f)
+{
+ register struct scsi_inquiry *inq = usalp->inq;
+
+ fprintf(f, "Device type : ");
+ usal_fprintdev(f, inq);
+ fprintf(f, "Version : %d\n", inq->ansi_version);
+ fprintf(f, "Response Format: %d\n", inq->data_format);
+ if (inq->data_format >= 2) {
+ fprintf(f, "Capabilities : ");
+ if (inq->aenc) fprintf(f, "AENC ");
+ if (inq->termiop) fprintf(f, "TERMIOP ");
+ if (inq->reladr) fprintf(f, "RELADR ");
+ if (inq->wbus32) fprintf(f, "WBUS32 ");
+ if (inq->wbus16) fprintf(f, "WBUS16 ");
+ if (inq->sync) fprintf(f, "SYNC ");
+ if (inq->linked) fprintf(f, "LINKED ");
+ if (inq->cmdque) fprintf(f, "CMDQUE ");
+ if (inq->softreset) fprintf(f, "SOFTRESET ");
+ fprintf(f, "\n");
+ }
+ if (inq->add_len >= 31 ||
+ inq->vendor_info[0] ||
+ inq->prod_ident[0] ||
+ inq->prod_revision[0]) {
+ fprintf(f, "Vendor_info : '%.8s'\n", inq->vendor_info);
+ fprintf(f, "Identification : '%.16s'\n", inq->prod_ident);
+ fprintf(f, "Revision : '%.4s'\n", inq->prod_revision);
+ }
+}
+
+void
+printdev(SCSI *usalp)
+{
+ printf("Device seems to be: ");
+
+ switch (usalp->dev) {
+
+ case DEV_UNKNOWN: printf("unknown"); break;
+ case DEV_ACB40X0: printf("Adaptec 4000/4010/4070"); break;
+ case DEV_ACB4000: printf("Adaptec 4000"); break;
+ case DEV_ACB4010: printf("Adaptec 4010"); break;
+ case DEV_ACB4070: printf("Adaptec 4070"); break;
+ case DEV_ACB5500: printf("Adaptec 5500"); break;
+ case DEV_ACB4520A: printf("Adaptec 4520A"); break;
+ case DEV_ACB4525: printf("Adaptec 4525"); break;
+ case DEV_MD21: printf("Emulex MD21"); break;
+ case DEV_MD23: printf("Emulex MD23"); break;
+ case DEV_NON_CCS_DSK: printf("Generic NON CCS Disk"); break;
+ case DEV_CCS_GENDISK: printf("Generic CCS Disk"); break;
+ case DEV_SONY_SMO: printf("Sony SMO-C501"); break;
+ case DEV_MT02: printf("Emulex MT02"); break;
+ case DEV_SC4000: printf("Sysgen SC4000"); break;
+ case DEV_RXT800S: printf("Maxtor RXT800S"); break;
+ case DEV_HRSCAN: printf("Berthold HR-Scanner"); break;
+ case DEV_MS300A: printf("Microtek MS300A"); break;
+
+ case DEV_CDROM: printf("Generic CD-ROM"); break;
+ case DEV_MMC_CDROM: printf("Generic mmc CD-ROM"); break;
+ case DEV_MMC_CDR: printf("Generic mmc CD-R"); break;
+ case DEV_MMC_CDRW: printf("Generic mmc CD-RW"); break;
+ case DEV_MMC_DVD: printf("Generic mmc2 DVD-ROM"); break;
+ case DEV_MMC_DVD_WR: printf("Generic mmc2 DVD-R/DVD-RW"); break;
+ case DEV_CDD_521_OLD: printf("Philips old CDD-521"); break;
+ case DEV_CDD_521: printf("Philips CDD-521"); break;
+ case DEV_CDD_522: printf("Philips CDD-522"); break;
+ case DEV_PCD_600: printf("Kodak PCD-600"); break;
+ case DEV_CDD_2000: printf("Philips CDD-2000"); break;
+ case DEV_CDD_2600: printf("Philips CDD-2600"); break;
+ case DEV_YAMAHA_CDR_100:printf("Yamaha CDR-100"); break;
+ case DEV_YAMAHA_CDR_400:printf("Yamaha CDR-400"); break;
+ case DEV_PLASMON_RF_4100:printf("Plasmon RF-4100"); break;
+ case DEV_SONY_CDU_924: printf("Sony CDU-924S"); break;
+ case DEV_RICOH_RO_1060C:printf("Ricoh RO-1060C"); break;
+ case DEV_RICOH_RO_1420C:printf("Ricoh RO-1420C"); break;
+ case DEV_TEAC_CD_R50S: printf("Teac CD-R50S"); break;
+ case DEV_MATSUSHITA_7501:printf("Matsushita CW-7501"); break;
+ case DEV_MATSUSHITA_7502:printf("Matsushita CW-7502"); break;
+
+ case DEV_PIONEER_DW_S114X: printf("Pioneer DW-S114X"); break;
+ case DEV_PIONEER_DVDR_S101:printf("Pioneer DVD-R S101"); break;
+
+ default: printf("Missing Entry for dev %d",
+ usalp->dev); break;
+
+ }
+ printf(".\n");
+
+}
+
+BOOL
+do_inquiry(SCSI *usalp, int print)
+{
+ if (getdev(usalp, print)) {
+ if (print)
+ printdev(usalp);
+ return (TRUE);
+ } else {
+ return (FALSE);
+ }
+}
+
+BOOL
+recovery_needed(SCSI *usalp, cdr_t *dp)
+{
+ int err;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ usalp->silent++;
+ err = test_unit_ready(usalp);
+ usalp->silent--;
+
+ if (err >= 0)
+ return (FALSE);
+ else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */
+ return (FALSE);
+
+ if (scmd->sense.code < 0x70) /* non extended Sense */
+ return (FALSE);
+
+ /* XXX Old Philips code */
+ return (((struct scsi_ext_sense *)&scmd->sense)->sense_code == 0xD0);
+}
+
+int
+scsi_load(SCSI *usalp, cdr_t *dp)
+{
+ int key;
+ int code;
+
+ if ((dp->cdr_flags & CDR_CADDYLOAD) == 0) {
+ if (scsi_start_stop_unit(usalp, 1, 1, dp && (dp->cdr_cmdflags&F_IMMED)) >= 0)
+ return (0);
+ }
+
+ if (wait_unit_ready(usalp, 60))
+ return (0);
+
+ key = usal_sense_key(usalp);
+ code = usal_sense_code(usalp);
+
+ if (key == SC_NOT_READY && (code == 0x3A || code == 0x30)) {
+ errmsgno(EX_BAD, "Cannot load media with %s drive!\n",
+ (dp->cdr_flags & CDR_CADDYLOAD) ? "caddy" : "this");
+ errmsgno(EX_BAD, "Try to load media by hand.\n");
+ }
+ return (-1);
+}
+
+int
+scsi_unload(SCSI *usalp, cdr_t *dp)
+{
+ return (scsi_start_stop_unit(usalp, 0, 1, dp && (dp->cdr_cmdflags&F_IMMED)));
+}
+
+int
+scsi_cdr_write(SCSI *usalp,
+ caddr_t bp /* address of buffer */,
+ long sectaddr /* disk address (sector) to put */,
+ long size /* number of bytes to transfer */,
+ int blocks /* sector count */,
+ BOOL islast /* last write for track */)
+{
+ return (write_xg1(usalp, bp, sectaddr, size, blocks));
+}
+
+struct cd_mode_page_2A *
+mmc_cap(SCSI *usalp, Uchar *modep)
+{
+ int len;
+ int val;
+ Uchar mode[0x100];
+ struct cd_mode_page_2A *mp;
+ struct cd_mode_page_2A *mp2;
+
+
+retry:
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ if (!get_mode_params(usalp, 0x2A, "CD capabilities",
+ mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) {
+
+ if (usal_sense_key(usalp) == SC_NOT_READY) {
+ if (wait_unit_ready(usalp, 60))
+ goto retry;
+ }
+ return (NULL); /* Pre SCSI-3/mmc drive */
+ }
+
+ if (len == 0) /* Pre SCSI-3/mmc drive */
+ return (NULL);
+
+ mp = (struct cd_mode_page_2A *)
+ (mode + sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len);
+
+ /*
+ * Do some heuristics against pre SCSI-3/mmc VU page 2A
+ * We should test for a minimum p_len of 0x14, but some
+ * buggy CD-ROM readers ommit the write speed values.
+ */
+ if (mp->p_len < 0x10)
+ return (NULL);
+
+ val = a_to_u_2_byte(mp->max_read_speed);
+ if (val != 0 && val < 176)
+ return (NULL);
+
+ val = a_to_u_2_byte(mp->cur_read_speed);
+ if (val != 0 && val < 176)
+ return (NULL);
+
+ len -= sizeof (struct scsi_mode_header) +
+ ((struct scsi_mode_header *)mode)->blockdesc_len;
+ if (modep)
+ mp2 = (struct cd_mode_page_2A *)modep;
+ else
+ mp2 = malloc(len);
+ if (mp2)
+ movebytes(mp, mp2, len);
+
+ return (mp2);
+}
+
+void
+mmc_getval(struct cd_mode_page_2A *mp,
+ BOOL *cdrrp /* CD ROM */,
+ BOOL *cdwrp /* CD-R writer */,
+ BOOL *cdrrwp /* CD-RW reader */,
+ BOOL *cdwrwp /* CD-RW writer */,
+ BOOL *dvdp /* DVD reader */,
+ BOOL *dvdwp /* DVD writer */)
+{
+ BOOL isdvd; /* Any DVD reader */
+ BOOL isdvd_wr; /* DVD writer (R / RAM) */
+ BOOL iscd_wr; /* CD writer */
+
+ iscd_wr = (mp->cd_r_write != 0) || /* SCSI-3/mmc CD-R */
+ (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */
+
+ if (cdrrp)
+ *cdrrp = (mp->cd_r_read != 0); /* SCSI-3/mmc CD */
+ if (cdwrp)
+ *cdwrp = (mp->cd_r_write != 0); /* SCSI-3/mmc CD-R */
+
+ if (cdrrwp)
+ *cdrrwp = (mp->cd_rw_read != 0); /* SCSI-3/mmc CD */
+ if (cdwrwp)
+ *cdwrwp = (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */
+
+ isdvd = /* SCSI-3/mmc2 DVD */
+ (mp->dvd_ram_read + mp->dvd_r_read +
+ mp->dvd_rom_read) != 0;
+
+ isdvd_wr = /* SCSI-3/mmc2 DVD writer*/
+ (mp->dvd_ram_write + mp->dvd_r_write) != 0;
+
+ if (dvdp)
+ *dvdp = isdvd;
+ if (dvdwp)
+ *dvdwp = isdvd_wr;
+}
+
+BOOL
+is_mmc(SCSI *usalp, BOOL *cdwp, BOOL *dvdwp)
+{
+ BOOL cdwr = FALSE;
+ BOOL cdwrw = FALSE;
+
+ if (cdwp)
+ *cdwp = FALSE;
+ if (dvdwp)
+ *dvdwp = FALSE;
+
+ if (!mmc_check(usalp, NULL, &cdwr, NULL, &cdwrw, NULL, dvdwp))
+ return (FALSE);
+
+ if (cdwp)
+ *cdwp = cdwr | cdwrw;
+
+ return (TRUE);
+}
+
+BOOL
+mmc_check(SCSI *usalp,
+ BOOL *cdrrp /* CD ROM */,
+ BOOL *cdwrp /* CD-R writer */,
+ BOOL *cdrrwp /* CD-RW reader */,
+ BOOL *cdwrwp /* CD-RW writer */,
+ BOOL *dvdp /* DVD reader */,
+ BOOL *dvdwp /* DVD writer */)
+{
+ Uchar mode[0x100];
+ BOOL was_atapi;
+ struct cd_mode_page_2A *mp;
+
+ if (usalp->inq->type != INQ_ROMD)
+ return (FALSE);
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ was_atapi = allow_atapi(usalp, TRUE);
+ usalp->silent++;
+ mp = mmc_cap(usalp, mode);
+ usalp->silent--;
+ allow_atapi(usalp, was_atapi);
+ if (mp == NULL)
+ return (FALSE);
+
+ mmc_getval(mp, cdrrp, cdwrp, cdrrwp, cdwrwp, dvdp, dvdwp);
+
+ return (TRUE); /* Generic SCSI-3/mmc CD */
+}
+
+static void
+print_speed(char *fmt, int val)
+{
+ printf(" %s: %5d kB/s", fmt, val);
+ printf(" (CD %3ux,", val/176);
+ printf(" DVD %2ux)\n", val/1385);
+}
+
+#define DOES(what, flag) printf(" Does %s%s\n", flag?"":"not ", what)
+#define IS(what, flag) printf(" Is %s%s\n", flag?"":"not ", what)
+#define VAL(what, val) printf(" %s: %d\n", what, val[0]*256 + val[1])
+#define SVAL(what, val) printf(" %s: %s\n", what, val)
+
+void
+print_capabilities(SCSI *usalp)
+{
+ BOOL was_atapi;
+ Uchar mode[0x100];
+ struct cd_mode_page_2A *mp;
+static const char *bclk[4] = {"32", "16", "24", "24 (I2S)"};
+static const char *load[8] = {"caddy", "tray", "pop-up", "reserved(3)",
+ "disc changer", "cartridge changer",
+ "reserved(6)", "reserved(7)" };
+static const char *rotctl[4] = {"CLV/PCAV", "CAV", "reserved(2)", "reserved(3)"};
+
+
+ if (usalp->inq->type != INQ_ROMD)
+ return;
+
+ fillbytes((caddr_t)mode, sizeof (mode), '\0');
+
+ was_atapi = allow_atapi(usalp, TRUE); /* Try to switch to 10 byte mode cmds */
+ usalp->silent++;
+ mp = mmc_cap(usalp, mode);
+ usalp->silent--;
+ allow_atapi(usalp, was_atapi);
+ if (mp == NULL)
+ return;
+
+ printf("\nDrive capabilities, per");
+ if (mp->p_len >= 28)
+ printf(" MMC-3");
+ else if (mp->p_len >= 24)
+ printf(" MMC-2");
+ else
+ printf(" MMC");
+ printf(" page 2A:\n\n");
+
+ DOES("read CD-R media", mp->cd_r_read);
+ DOES("write CD-R media", mp->cd_r_write);
+ DOES("read CD-RW media", mp->cd_rw_read);
+ DOES("write CD-RW media", mp->cd_rw_write);
+ DOES("read DVD-ROM media", mp->dvd_rom_read);
+ DOES("read DVD-R media", mp->dvd_r_read);
+ DOES("write DVD-R media", mp->dvd_r_write);
+ DOES("read DVD-RAM media", mp->dvd_ram_read);
+ DOES("write DVD-RAM media", mp->dvd_ram_write);
+ DOES("support test writing", mp->test_write);
+ printf("\n");
+ DOES("read Mode 2 Form 1 blocks", mp->mode_2_form_1);
+ DOES("read Mode 2 Form 2 blocks", mp->mode_2_form_2);
+ DOES("read digital audio blocks", mp->cd_da_supported);
+ if (mp->cd_da_supported)
+ DOES("restart non-streamed digital audio reads accurately", mp->cd_da_accurate);
+ DOES("support Buffer-Underrun-Free recording", mp->BUF);
+ DOES("read multi-session CDs", mp->multi_session);
+ DOES("read fixed-packet CD media using Method 2", mp->method2);
+ DOES("read CD bar code", mp->read_bar_code);
+ DOES("read R-W subcode information", mp->rw_supported);
+ if (mp->rw_supported)
+ DOES("return R-W subcode de-interleaved and error-corrected", mp->rw_deint_corr);
+ DOES("read raw P-W subcode data from lead in", mp->pw_in_lead_in);
+ DOES("return CD media catalog number", mp->UPC);
+ DOES("return CD ISRC information", mp->ISRC);
+ DOES("support C2 error pointers", mp->c2_pointers);
+ DOES("deliver composite A/V data", mp->composite);
+ printf("\n");
+ DOES("play audio CDs", mp->audio_play);
+ if (mp->audio_play) {
+ VAL("Number of volume control levels", mp->num_vol_levels);
+ DOES("support individual volume control setting for each channel", mp->sep_chan_vol);
+ DOES("support independent mute setting for each channel", mp->sep_chan_mute);
+ DOES("support digital output on port 1", mp->digital_port_1);
+ DOES("support digital output on port 2", mp->digital_port_2);
+ if (mp->digital_port_1 || mp->digital_port_2) {
+ DOES("send digital data LSB-first", mp->LSBF);
+ DOES("set LRCK high for left-channel data", mp->RCK);
+ DOES("have valid data on falling edge of clock", mp->BCK);
+ SVAL("Length of data in BCLKs", bclk[mp->length]);
+ }
+ }
+ printf("\n");
+ SVAL("Loading mechanism type", load[mp->loading_type]);
+ DOES("support ejection of CD via START/STOP command", mp->eject);
+ DOES("lock media on power up via prevent jumper", mp->prevent_jumper);
+ DOES("allow media to be locked in the drive via PREVENT/ALLOW command", mp->lock);
+ IS("currently in a media-locked state", mp->lock_state);
+ DOES("support changing side of disk", mp->side_change);
+ DOES("have load-empty-slot-in-changer feature", mp->sw_slot_sel);
+ DOES("support Individual Disk Present feature", mp->disk_present_rep);
+ printf("\n");
+ print_speed("Maximum read speed", a_to_u_2_byte(mp->max_read_speed));
+ print_speed("Current read speed", a_to_u_2_byte(mp->cur_read_speed));
+ print_speed("Maximum write speed", a_to_u_2_byte(mp->max_write_speed));
+ if (mp->p_len >= 28)
+ print_speed("Current write speed", a_to_u_2_byte(mp->v3_cur_write_speed));
+ else
+ print_speed("Current write speed", a_to_u_2_byte(mp->cur_write_speed));
+ if (mp->p_len >= 28) {
+ SVAL("Rotational control selected", rotctl[mp->rot_ctl_sel]);
+ }
+ VAL("Buffer size in KB", mp->buffer_size);
+
+ if (mp->p_len >= 24) {
+ VAL("Copy management revision supported", mp->copy_man_rev);
+ }
+
+ if (mp->p_len >= 28) {
+ struct cd_wr_speed_performance *pp;
+ Uint ndesc;
+ Uint i;
+ Uint n;
+
+ ndesc = a_to_u_2_byte(mp->num_wr_speed_des);
+ pp = mp->wr_speed_des;
+ printf(" Number of supported write speeds: %d\n", ndesc);
+ for (i = 0; i < ndesc; i++, pp++) {
+ printf(" Write speed # %d:", i);
+ n = a_to_u_2_byte(pp->wr_speed_supp);
+ printf(" %5d kB/s", n);
+ printf(" %s", rotctl[pp->rot_ctl_sel]);
+ printf(" (CD %3ux,", n/176);
+ printf(" DVD %2ux)\n", n/1385);
+ }
+ }
+
+ /* Generic SCSI-3/mmc CD */
+}
diff --git a/wodim/scsi_cdr_mmc4.c b/wodim/scsi_cdr_mmc4.c
new file mode 100644
index 0000000..cb0958a
--- /dev/null
+++ b/wodim/scsi_cdr_mmc4.c
@@ -0,0 +1,80 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_cdr_mmc4.c 1.1 05/05/16 Copyright 1995-2005 J. Schilling */
+/*
+ * SCSI command functions for cdrecord
+ * covering MMC-4
+ *
+ * Copyright (c) 1995-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsimmc.h"
+#include "wodim.h"
+
+void print_capabilities_mmc4(SCSI *usalp);
+
+#define DOES(what, flag) printf(" Does %s%s\n", flag?"":"not ", what)
+
+
+void
+print_capabilities_mmc4(SCSI *usalp)
+{
+ int cdrw_types;
+
+ if (usalp->inq->type != INQ_ROMD)
+ return;
+
+ cdrw_types = get_supported_cdrw_media_types(usalp);
+ if (cdrw_types != -1) {
+ printf("\nSupported CD-RW media types according to MMC-4 feature 0x37:\n");
+ DOES("write multi speed CD-RW media", (cdrw_types & CDR_CDRW_MULTI));
+ DOES("write high speed CD-RW media", (cdrw_types & CDR_CDRW_HIGH));
+ DOES("write ultra high speed CD-RW media", (cdrw_types & CDR_CDRW_ULTRA));
+ DOES("write ultra high speed+ CD-RW media", (cdrw_types & CDR_CDRW_ULTRAP));
+ }
+}
diff --git a/wodim/scsi_mmc.c b/wodim/scsi_mmc.c
new file mode 100644
index 0000000..f8baa1f
--- /dev/null
+++ b/wodim/scsi_mmc.c
@@ -0,0 +1,411 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_mmc.c 1.13 05/05/16 Copyright 2002-2005 J. Schilling */
+/*
+ * SCSI command functions for cdrecord
+ * covering MMC-3 level and above
+ *
+ * Copyright (c) 2002-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+Includes code from http://libburnia.pykix.org/browser/libburn/trunk/libburn/mmc.c?format=txt
+*/
+
+/*#define DEBUG*/
+
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsimmc.h"
+#include "wodim.h"
+
+extern int xdebug;
+
+
+
+int get_configuration(SCSI *usalp, caddr_t bp, int cnt, int st_feature,
+ int rt);
+static int get_conflen(SCSI *usalp, int st_feature, int rt);
+int get_curprofile(SCSI *usalp);
+static int get_profiles(SCSI *usalp, caddr_t bp, int cnt);
+int print_profiles(SCSI *usalp);
+int get_proflist(SCSI *usalp, BOOL *wp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp,
+ BOOL *ddcdp);
+int get_wproflist(SCSI *usalp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp,
+ BOOL *ddcdp);
+
+/*
+ * Get feature codes
+ */
+int
+get_configuration(SCSI *usalp, caddr_t bp, int cnt, int st_feature, int rt)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof (*scmd), '\0');
+ scmd->addr = bp;
+ scmd->size = cnt;
+ scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x46;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ if (rt & 1)
+ scmd->cdb.g1_cdb.reladr = 1;
+ if (rt & 2)
+ scmd->cdb.g1_cdb.res = 1;
+
+ i_to_2_byte(scmd->cdb.g1_cdb.addr, st_feature);
+ g1_cdblen(&scmd->cdb.g1_cdb, cnt);
+
+ usalp->cmdname = "get_configuration";
+
+ return (usal_cmd(usalp));
+}
+
+/*
+ * Retrieve feature code list length
+ */
+static int
+get_conflen(SCSI *usalp, int st_feature, int rt)
+{
+ Uchar cbuf[8];
+ int flen;
+ int i;
+
+ fillbytes(cbuf, sizeof (cbuf), '\0');
+ usalp->silent++;
+ i = get_configuration(usalp, (char *)cbuf, sizeof (cbuf), st_feature, rt);
+ usalp->silent--;
+ if (i < 0)
+ return (-1);
+ i = sizeof (cbuf) - usal_getresid(usalp);
+ if (i < 4)
+ return (-1);
+
+ flen = a_to_u_4_byte(cbuf);
+ if (flen < 4)
+ return (-1);
+ return (flen);
+}
+
+int
+get_curprofile(SCSI *usalp)
+{
+ Uchar cbuf[8];
+ int amt;
+ int flen;
+ int profile;
+ int i;
+
+ fillbytes(cbuf, sizeof (cbuf), '\0');
+ usalp->silent++;
+ i = get_configuration(usalp, (char *)cbuf, sizeof (cbuf), 0, 0);
+ usalp->silent--;
+ if (i < 0)
+ return (-1);
+
+ amt = sizeof (cbuf) - usal_getresid(usalp);
+ if (amt < 8)
+ return (-1);
+ flen = a_to_u_4_byte(cbuf);
+ if (flen < 4)
+ return (-1);
+
+ profile = a_to_u_2_byte(&cbuf[6]);
+
+ if (xdebug > 1)
+ usal_prbytes("Features: ", cbuf, amt);
+
+ if (xdebug > 0)
+ printf("feature len: %d current profile 0x%04X (%s) len %d\n",
+ flen, profile,
+ mmc_obtain_profile_name(profile), amt);
+
+ return (profile);
+}
+
+static int
+get_profiles(SCSI *usalp, caddr_t bp, int cnt)
+{
+ int amt;
+ int flen;
+ int i;
+
+ flen = get_conflen(usalp, 0, 0);
+ if (flen < 0)
+ return (-1);
+ if (cnt < flen)
+ flen = cnt;
+
+ fillbytes(bp, cnt, '\0');
+ usalp->silent++;
+ i = get_configuration(usalp, (char *)bp, flen, 0, 0);
+ usalp->silent--;
+ if (i < 0)
+ return (-1);
+ amt = flen - usal_getresid(usalp);
+
+ flen = a_to_u_4_byte(bp);
+ if ((flen+4) < amt)
+ amt = flen+4;
+
+ return (amt);
+}
+
+int
+print_profiles(SCSI *usalp)
+{
+ Uchar cbuf[1024];
+ Uchar *p;
+ int flen;
+ int curprofile;
+ int profile;
+ int i;
+ int n;
+
+ flen = get_profiles(usalp, (caddr_t)cbuf, sizeof (cbuf));
+ if (flen < 0)
+ return (-1);
+
+ p = cbuf;
+ if (xdebug > 1)
+ usal_prbytes("Features: ", cbuf, flen);
+
+ curprofile = a_to_u_2_byte(&p[6]);
+ if (xdebug > 0)
+ printf("feature len: %d current profile 0x%04X (%s)\n",
+ flen, curprofile,
+ mmc_obtain_profile_name(curprofile));
+
+ printf("Current: 0x%04X (%s)\n", curprofile,
+ mmc_obtain_profile_name(curprofile));
+
+ p += 8; /* Skip feature header */
+ n = p[3]; /* Additional length */
+ n /= 4;
+ p += 4;
+
+ for (i = 0; i < n; i++) {
+ profile = a_to_u_2_byte(p);
+ if (xdebug > 0)
+ printf("Profile: 0x%04X (%s)", profile,
+ mmc_obtain_profile_name(profile));
+ else
+ printf("Profile: ");
+ printf("0x%04X (%s) %s\n", profile,
+ mmc_obtain_profile_name(profile),
+ p[2] & 1 ? "(current)":"");
+ p += 4;
+ }
+ return (curprofile);
+}
+
+int
+get_proflist(SCSI *usalp, BOOL *wp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp,
+ BOOL *ddcdp)
+{
+ Uchar cbuf[1024];
+ Uchar *p;
+ int flen;
+ int curprofile;
+ int profile;
+ int i;
+ int n;
+ BOOL wr = FALSE;
+ BOOL cd = FALSE;
+ BOOL dvd = FALSE;
+ BOOL dvdplus = FALSE;
+ BOOL ddcd = FALSE;
+
+ flen = get_profiles(usalp, (caddr_t)cbuf, sizeof (cbuf));
+ if (flen < 0)
+ return (-1);
+
+ p = cbuf;
+ if (xdebug > 1)
+ usal_prbytes("Features: ", cbuf, flen);
+
+ curprofile = a_to_u_2_byte(&p[6]);
+ if (xdebug > 0)
+ printf("feature len: %d current profile 0x%04X (%s)\n",
+ flen, curprofile,
+ mmc_obtain_profile_name(curprofile));
+
+ p += 8; /* Skip feature header */
+ n = p[3]; /* Additional length */
+ n /= 4;
+ p += 4;
+
+ for (i = 0; i < n; i++) {
+ profile = a_to_u_2_byte(p);
+ p += 4;
+ if (profile >= 0x0008 && profile < 0x0010)
+ cd = TRUE;
+ if (profile > 0x0008 && profile < 0x0010)
+ wr = TRUE;
+
+ if (profile >= 0x0010 && profile < 0x0018)
+ dvd = TRUE;
+ if (profile > 0x0010 && profile < 0x0018)
+ wr = TRUE;
+
+ if (profile >= 0x0018 && profile < 0x0020)
+ dvdplus = TRUE;
+ if (profile > 0x0018 && profile < 0x0020)
+ wr = TRUE;
+
+ if (profile >= 0x0020 && profile < 0x0028)
+ ddcd = TRUE;
+ if (profile > 0x0020 && profile < 0x0028)
+ wr = TRUE;
+ }
+ if (wp)
+ *wp = wr;
+ if (cdp)
+ *cdp = cd;
+ if (dvdp)
+ *dvdp = dvd;
+ if (dvdplusp)
+ *dvdplusp = dvdplus;
+ if (ddcdp)
+ *ddcdp = ddcd;
+
+ return (curprofile);
+}
+
+int
+get_wproflist(SCSI *usalp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp, BOOL *ddcdp)
+{
+ Uchar cbuf[1024];
+ Uchar *p;
+ int flen;
+ int curprofile;
+ int profile;
+ int i;
+ int n;
+ BOOL cd = FALSE;
+ BOOL dvd = FALSE;
+ BOOL dvdplus = FALSE;
+ BOOL ddcd = FALSE;
+
+ flen = get_profiles(usalp, (caddr_t)cbuf, sizeof (cbuf));
+ if (flen < 0)
+ return (-1);
+ p = cbuf;
+ curprofile = a_to_u_2_byte(&p[6]);
+
+ p += 8; /* Skip feature header */
+ n = p[3]; /* Additional length */
+ n /= 4;
+ p += 4;
+
+ for (i = 0; i < n; i++) {
+ profile = a_to_u_2_byte(p);
+ p += 4;
+ if (profile > 0x0008 && profile < 0x0010)
+ cd = TRUE;
+ if (profile > 0x0010 && profile < 0x0018)
+ dvd = TRUE;
+ if (profile > 0x0018 && profile < 0x0020)
+ dvdplus = TRUE;
+ if (profile > 0x0020 && profile < 0x0028)
+ ddcd = TRUE;
+ }
+ if (cdp)
+ *cdp = cd;
+ if (dvdp)
+ *dvdp = dvd;
+ if (dvdplusp)
+ *dvdplusp = dvdplus;
+ if (ddcdp)
+ *ddcdp = ddcd;
+
+ return (curprofile);
+}
+
+
+char *mmc_obtain_profile_name(int profile_number) {
+ static char *texts[0x53] = {NULL};
+ static char *reserved="Reserved/Unknown";
+ int i, max_pno = 0x53;
+
+ if (texts[0] == NULL) {
+ for (i = 0; i<max_pno; i++)
+ texts[i] = reserved;
+ /* mmc5r04c.pdf , Table 88, human readable profile names */
+ texts[0x01] = "Non-removable disk";
+ texts[0x02] = "Removable disk";
+ texts[0x03] = "MO erasable";
+ texts[0x04] = "Optical write once";
+ texts[0x05] = "AS-MO";
+ texts[0x08] = "CD-ROM";
+ texts[0x09] = "CD-R";
+ texts[0x0a] = "CD-RW";
+ texts[0x10] = "DVD-ROM";
+ texts[0x11] = "DVD-R sequential recording";
+ texts[0x12] = "DVD-RAM";
+ texts[0x13] = "DVD-RW restricted overwrite";
+ texts[0x14] = "DVD-RW sequential recording";
+ texts[0x15] = "DVD-R/DL sequential recording";
+ texts[0x16] = "DVD-R/DL layer jump recording";
+ texts[0x1a] = "DVD+RW";
+ texts[0x1b] = "DVD+R";
+ texts[0x2a] = "DVD+RW/DL";
+ texts[0x2b] = "DVD+R/DL";
+ texts[0x40] = "BD-ROM";
+ texts[0x41] = "BD-R sequential recording";
+ texts[0x42] = "BD-R random recording";
+ texts[0x43] = "BD-RE";
+ texts[0x50] = "HD-DVD-ROM";
+ texts[0x51] = "HD-DVD-R";
+ texts[0x52] = "HD-DVD-RAM";
+ }
+ if (profile_number<0 || profile_number>=max_pno)
+ return "";
+ return texts[profile_number];
+}
+
diff --git a/wodim/scsi_mmc4.c b/wodim/scsi_mmc4.c
new file mode 100644
index 0000000..1257093
--- /dev/null
+++ b/wodim/scsi_mmc4.c
@@ -0,0 +1,83 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_mmc4.c 1.1 05/05/16 Copyright 2002-2005 J. Schilling */
+/*
+ * SCSI command functions for cdrecord
+ * covering MMC-4 level and above
+ *
+ * Copyright (c) 2002-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/* #define DEBUG */
+#include <mconfig.h>
+
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <strdefs.h>
+#include <timedefs.h>
+
+#include <utypes.h>
+#include <btorder.h>
+#include <intcvt.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsimmc.h"
+#include "wodim.h"
+
+ int get_supported_cdrw_media_types(SCSI *usalp);
+
+/*
+ * Retrieve list of supported cd-rw media types (feature 0x37)
+ */
+int
+get_supported_cdrw_media_types(SCSI *usalp)
+{
+ Uchar cbuf[16];
+ int ret;
+ fillbytes(cbuf, sizeof (cbuf), '\0');
+
+ usalp->silent++;
+ ret = get_configuration(usalp, (char *)cbuf, sizeof (cbuf), 0x37, 2);
+ usalp->silent--;
+
+ if (ret < 0)
+ return (-1);
+
+ if (cbuf[3] < 12) /* Couldn't retrieve feature 0x37 */
+ return (-1);
+
+ return (int)(cbuf[13]);
+}
diff --git a/wodim/scsi_scan.c b/wodim/scsi_scan.c
new file mode 100644
index 0000000..218d23f
--- /dev/null
+++ b/wodim/scsi_scan.c
@@ -0,0 +1,379 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_scan.c 1.19 04/04/16 Copyright 1997-2004 J. Schilling */
+/*
+ * Scan SCSI Bus.
+ * Stolen from sformat. Need a more general form to
+ * re-use it in sformat too.
+ *
+ * Copyright (c) 1997-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <errno.h>
+#include <schily.h>
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+#include <usal/scsitransp.h>
+
+#include "scsi_scan.h"
+#include "wodim.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+
+static void print_product(FILE *f, struct scsi_inquiry *ip);
+int select_target(SCSI *usalp, FILE *f);
+
+#define MAXDEVCOUNT (256+26)
+
+extern BOOL check_linux_26();
+
+static void print_product(FILE *f, struct scsi_inquiry *ip) {
+ fprintf(f, "'%.8s' ", ip->vendor_info);
+ fprintf(f, "'%.16s' ", ip->prod_ident);
+ fprintf(f, "'%.4s' ", ip->prod_revision);
+ if (ip->add_len < 31) {
+ fprintf(f, "NON CCS ");
+ }
+ usal_fprintdev(f, ip);
+}
+
+SCSI * open_auto(int64_t need_size, int debug, int lverbose) {
+ int res;
+ SCSI * usalp = NULL;
+ char errstr[80];
+
+#ifdef __linux__
+ /* quick-and-dirty code but should do what is supposed to, more quickly */
+
+ /*
+ * For Linux, try these strategies, in order:
+ * 1. stat /dev/cdrw or /dev/dvdrw, depending on size we need.
+ * 2. Read /proc/sys/dev/cdrom/info, look for a CD-R/DVD-R.
+ * Will fail for kernel 2.4 or if cdrom module not loaded.
+ * 3. stat /dev/cdrom, just assume that it can write media.
+
+ An example for procfs file contents, beware of the TABs
+
+---
+CD-ROM information, Id: cdrom.c 3.20 2003/12/17
+
+drive name: hdc hda
+drive speed: 40 40
+drive # of slots: 1 1
+Can close tray: 1 1
+Can open tray: 1 1
+Can lock tray: 1 1
+Can change speed: 1 1
+Can select disk: 0 0
+Can read multisession: 1 1
+Can read MCN: 1 1
+Reports media changed: 1 1
+Can play audio: 1 1
+Can write CD-R: 0 1
+Can write CD-RW: 0 1
+Can read DVD: 1 1
+Can write DVD-R: 0 1
+Can write DVD-RAM: 0 1
+Can read MRW: 0 1
+Can write MRW: 0 1
+Can write RAM: 0 1
+
+---
+*/
+ struct stat statbuf;
+ /* XXX Good guess? BD-RE recorders may not support CDRW anymore... */
+ char *type="CD-R", *key="Can write CD-R:", *guessdev="/dev/cdrw", *result=NULL;
+ FILE *fh;
+
+ if( need_size > 360000*2048 ) {
+ type="DVD-R";
+ guessdev="/dev/dvdrw";
+ key="Can write DVD-R:";
+ }
+
+ if(need_size>10240) /* don't bother with weird numbers */
+ fprintf(stderr, "Looking for a %s drive to store %.2f MiB...\n", type, (float)need_size/1048576.0);
+ if(0==stat(guessdev, &statbuf))
+ result=guessdev;
+ else if(0!= (fh = fopen("/proc/sys/dev/cdrom/info", "r")) ) {
+ /* ok, going the hard way */
+ char *nameline=NULL;
+ static char buf[256];
+ int kn = strlen(key);
+
+ buf[255]='\0';
+
+ while(fgets(buf, sizeof(buf), fh)) {
+ if(0==strncmp(buf, "drive name:", 11))
+ nameline=strdup(buf);
+ if(nameline && 0==strncmp(buf, key, kn)) {
+ int p=kn;
+ char *descptr=nameline+11; /* start at the known whitespace */
+ while(p<sizeof(buf) && buf[p]) {
+ if(buf[p]=='1' || buf[p]=='0') {
+ /* find the beginning of the descriptor */
+ for(;isspace((Uchar) *descptr);descptr++)
+ ;
+ }
+ if(buf[p]=='1') {
+ result=descptr-5;
+ /* terminate on space/newline and stop there */
+ for(;*descptr;descptr++) {
+ if(isspace((Uchar) *descptr))
+ *(descptr--)='\0';
+ }
+ strncpy(result, "/dev/", 5);
+ break;
+ }
+ else { /* no hit, move to after word ending */
+ for(; *descptr && ! isspace((Uchar) *descptr); descptr++)
+ ;
+ }
+ p++;
+ }
+ }
+
+ }
+ fclose(fh);
+ }
+
+ if(result)
+ fprintf(stderr, "Detected %s drive: %s\n", type, result);
+ if (0==stat("/dev/cdrom", &statbuf)) {
+ result = "/dev/cdrom";
+ fprintf(stderr, "Using /dev/cdrom of unknown capabilities\n");
+ }
+ if(result)
+ return usal_open(result, errstr, sizeof(errstr), debug, lverbose);
+#endif /* __linux__ */
+
+ usalp = usal_open(NULL, errstr, sizeof(errstr), debug, lverbose);
+ if(!usalp)
+ return NULL;
+ res = list_devices(usalp, stdout, 1);
+ if(res>0)
+ return usalp;
+ else
+ usal_close(usalp);
+ return NULL;
+}
+
+int list_devices(SCSI *usalp, FILE *f, int pickup_first) {
+ int initiator;
+ int i;
+ int bus;
+ int tgt;
+ int lun = 0;
+ BOOL have_tgt;
+
+ int fd, ndevs=0;
+ struct stat statbuf;
+ char *lines[MAXDEVCOUNT];
+ char buf[256], perms[8];
+
+
+ usalp->silent++;
+
+ /* XXX should be done before opening usal fprintf(stderr, "Beginning native device scan. This may take a while if devices are busy...\n"); */
+
+ for (bus = 0; bus < 1256; bus++) {
+ usal_settarget(usalp, bus, 0, 0);
+
+ if (!usal_havebus(usalp, bus))
+ continue;
+
+ initiator = usal_initiator_id(usalp);
+ //fprintf(f, "scsibus%d:\n", bus);
+
+ for (tgt = 0; tgt < 16; tgt++) {
+ usal_settarget(usalp, bus, tgt, lun);
+ have_tgt = unit_ready(usalp) || usalp->scmd->error != SCG_FATAL;
+
+ if (!have_tgt && tgt > 7) {
+ if (usalp->scmd->ux_errno == EINVAL)
+ break;
+ continue;
+ }
+
+ fd=usal_fileno(usalp, bus, tgt, lun);
+ strcpy(perms,"------");
+ if(fd>=0 && 0==fstat(fd, &statbuf)) {
+ if(statbuf.st_mode&S_IRUSR) perms[0]= 'r';
+ if(statbuf.st_mode&S_IWUSR) perms[1]= 'w';
+ if(statbuf.st_mode&S_IRGRP) perms[2]= 'r';
+ if(statbuf.st_mode&S_IWGRP) perms[3]= 'w';
+ if(statbuf.st_mode&S_IROTH) perms[4]= 'r';
+ if(statbuf.st_mode&S_IWOTH) perms[5]= 'w';
+ }
+ getdev(usalp, FALSE);
+ if(usalp->inq->type == INQ_ROMD || usalp->inq->type == INQ_WORM) {
+ char *p;
+
+ for(p=usalp->inq->vendor_info + 7 ; p >= usalp->inq->vendor_info; p--) {
+ if(isspace((unsigned char)*p))
+ *p='\0';
+ else
+ break;
+ }
+ for(p=usalp->inq->prod_ident + 15 ; p >= usalp->inq->prod_ident; p--) {
+ if(isspace((unsigned char)*p))
+ *p='\0';
+ else
+ break;
+ }
+ snprintf(buf, sizeof(buf), "%2d dev='%s'\t%s : '%.8s' '%.16s'\n", ndevs, usal_natname(usalp, bus, tgt, lun), perms, usalp->inq->vendor_info, usalp->inq->prod_ident);
+ /* alternative use, only select the first device */
+ if(pickup_first) {
+ printf("Using drive: %s\n", usal_natname(usalp, bus, tgt, lun));
+ return 1;
+ }
+ lines[ndevs++]=strdup(buf);
+ }
+
+ }
+ }
+ usalp->silent--;
+
+ /* should have been returned before if there was a recorder */
+ if(pickup_first)
+ return 0;
+
+ /* now start the output */
+
+ fprintf(stdout, "%s: Overview of accessible drives (%d found) :\n"
+ "-------------------------------------------------------------------------\n",
+ get_progname(), ndevs);
+ for(i=0;i<ndevs;i++) {
+ fprintf(stdout, "%s", lines[i]);
+ free(lines[i]);
+ }
+ fprintf(stdout, "-------------------------------------------------------------------------\n");
+
+ return ndevs;
+}
+
+int select_target(SCSI *usalp, FILE *f) {
+ int initiator;
+#ifdef FMT
+ int cscsibus = usal_scsibus(usalp);
+ int ctarget = usal_target(usalp);
+ int clun = usal_lun(usalp);
+#endif
+ int n;
+ int low = -1;
+ int high = -1;
+ int amt = 0;
+ int bus;
+ int tgt;
+ int lun = 0;
+ BOOL have_tgt;
+
+ usalp->silent++;
+
+ for (bus = 0; bus < 1256; bus++) {
+ usal_settarget(usalp, bus, 0, 0);
+
+ if (!usal_havebus(usalp, bus))
+ continue;
+
+ initiator = usal_initiator_id(usalp);
+ fprintf(f, "scsibus%d:\n", bus);
+
+ for (tgt = 0; tgt < 16; tgt++) {
+ n = bus*100 + tgt;
+
+ usal_settarget(usalp, bus, tgt, lun);
+ have_tgt = unit_ready(usalp) || usalp->scmd->error != SCG_FATAL;
+
+ if (!have_tgt && tgt > 7) {
+ if (usalp->scmd->ux_errno == EINVAL)
+ break;
+ continue;
+ }
+
+#ifdef FMT
+ if (print_disknames(bus, tgt, -1) < 8)
+ fprintf(f, "\t");
+ else
+ fprintf(f, " ");
+#else
+ fprintf(f, "\t");
+#endif
+ if (fprintf(f, "%d,%d,%d", bus, tgt, lun) < 8)
+ fprintf(f, "\t");
+ else
+ fprintf(f, " ");
+ fprintf(f, "%3d) ", n);
+ if (tgt == initiator) {
+ fprintf(f, "HOST ADAPTOR\n");
+ continue;
+ }
+ if (!have_tgt) {
+ /*
+ * Hack: fd -> -2 means no access
+ */
+ fprintf(f, "%c\n", usalp->fd == -2 ? '?':'*');
+ continue;
+ }
+ amt++;
+ if (low < 0)
+ low = n;
+ high = n;
+
+ getdev(usalp, FALSE);
+ print_product(f, usalp->inq);
+ }
+ }
+ usalp->silent--;
+
+ if (low < 0) {
+ errmsgno(EX_BAD, "No target found.\n");
+ return (0);
+ }
+ n = -1;
+#ifdef FMT
+ getint("Select target", &n, low, high);
+ bus = n/100;
+ tgt = n%100;
+ usal_settarget(usalp, bus, tgt, lun);
+ return (select_unit(usalp));
+
+ usal_settarget(usalp, cscsibus, ctarget, clun);
+#endif
+ return (amt);
+}
+
diff --git a/wodim/scsi_scan.h b/wodim/scsi_scan.h
new file mode 100644
index 0000000..b1e055b
--- /dev/null
+++ b/wodim/scsi_scan.h
@@ -0,0 +1,44 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsi_scan.h 1.3 01/03/12 Copyright 1997 J. Schilling */
+/*
+ * Interface to scan SCSI Bus.
+ *
+ * Copyright (c) 1997 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _SCSI_SCAN_H
+#define _SCSI_SCAN_H
+
+#include <usal/scsitransp.h>
+
+extern int select_target(SCSI *usalp, FILE *f);
+extern int list_devices(SCSI *usalp, FILE *f, int pickup_type);
+extern SCSI * open_auto(int64_t need_size, int debug, int lverbose);
+extern int scsi_set_streaming(SCSI *usalp, caddr_t addr, int size);
+
+#endif /* _SCSI_SCAN_H */
diff --git a/wodim/scsimmc.h b/wodim/scsimmc.h
new file mode 100644
index 0000000..a0092fc
--- /dev/null
+++ b/wodim/scsimmc.h
@@ -0,0 +1,593 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)scsimmc.h 1.11 04/03/01 Copyright 1997-2004 J. Schilling */
+/*
+ * Definitions for SCSI/mmc compliant drives
+ *
+ * Copyright (c) 1997-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _SCSIMMC_H
+#define _SCSIMMC_H
+
+#include <utypes.h>
+#include <btorder.h>
+
+typedef struct opc {
+ Uchar opc_speed[2];
+ Uchar opc_val[6];
+} opc_t;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct disk_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Ucbit disk_status : 2; /* Status of the disk */
+ Ucbit sess_status : 2; /* Status of last session */
+ Ucbit erasable : 1; /* Disk is erasable */
+ Ucbit res2 : 3; /* Reserved */
+ Uchar first_track; /* # of first track on disk */
+ Uchar numsess; /* # of sessions */
+ Uchar first_track_ls; /* First track in last sessaion */
+ Uchar last_track_ls; /* Last track in last sessaion */
+ Ucbit bg_format_stat : 2; /* Background format status */
+ Ucbit dbit : 1; /* Dirty Bit of defect table */
+ Ucbit res7_34 : 2; /* Reserved */
+ Ucbit uru : 1; /* This is an unrestricted disk */
+ Ucbit dbc_v : 1; /* Disk bar code valid */
+ Ucbit did_v : 1; /* Disk id valid */
+ Uchar disk_type; /* Disk type */
+ Uchar res9[3]; /* Reserved */
+ Uchar disk_id[4]; /* Disk identification */
+ Uchar last_lead_in[4]; /* Last session lead in time */
+ Uchar last_lead_out[4]; /* Last session lead out time */
+ Uchar disk_barcode[8]; /* Disk bar code */
+ Uchar res32; /* Reserved */
+ Uchar num_opc_entries; /* # of OPC table entries */
+ opc_t opc_table[1]; /* OPC table */
+};
+
+#else /* Motorola bitorder */
+
+struct disk_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Ucbit res2 : 3; /* Reserved */
+ Ucbit erasable : 1; /* Disk is erasable */
+ Ucbit sess_status : 2; /* Status of last session */
+ Ucbit disk_status : 2; /* Status of the disk */
+ Uchar first_track; /* # of first track on disk */
+ Uchar numsess; /* # of sessions */
+ Uchar first_track_ls; /* First track in last sessaion */
+ Uchar last_track_ls; /* Last track in last sessaion */
+ Ucbit did_v : 1; /* Disk id valid */
+ Ucbit dbc_v : 1; /* Disk bar code valid */
+ Ucbit uru : 1; /* This is an unrestricted disk */
+ Ucbit res7_34 : 2; /* Reserved */
+ Ucbit dbit : 1; /* Dirty Bit of defect table */
+ Ucbit bg_format_stat : 2; /* Background format status */
+ Uchar disk_type; /* Disk type */
+ Uchar res9[3]; /* Reserved */
+ Uchar disk_id[4]; /* Disk identification */
+ Uchar last_lead_in[4]; /* Last session lead in time */
+ Uchar last_lead_out[4]; /* Last session lead out time */
+ Uchar disk_barcode[8]; /* Disk bar code */
+ Uchar res32; /* Reserved */
+ Uchar num_opc_entries; /* # of OPC table entries */
+ opc_t opc_table[1]; /* OPC table */
+};
+
+#endif
+
+struct cd_mode_data {
+ struct scsi_mode_header header;
+ union cd_pagex {
+ struct cd_mode_page_05 page05;
+ struct cd_mode_page_2A page2A;
+ } pagex;
+};
+
+struct tocheader {
+ Uchar len[2];
+ Uchar first;
+ Uchar last;
+};
+
+/*
+ * Full TOC entry
+ */
+struct ftrackdesc {
+ Uchar sess_number;
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */
+ Ucbit control : 4;
+ Ucbit adr : 4;
+#else /* Motorola byteorder */
+ Ucbit adr : 4;
+ Ucbit control : 4;
+#endif
+
+ Uchar track;
+ Uchar point;
+ Uchar amin;
+ Uchar asec;
+ Uchar aframe;
+ Uchar res7;
+ Uchar pmin;
+ Uchar psec;
+ Uchar pframe;
+};
+
+struct fdiskinfo {
+ struct tocheader hd;
+ struct ftrackdesc desc[1];
+};
+
+
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct atipdesc {
+ Ucbit ref_speed : 3; /* Reference speed */
+ Ucbit res4_3 : 1; /* Reserved */
+ Ucbit ind_wr_power : 3; /* Indicative tgt writing power */
+ Ucbit res4_7 : 1; /* Reserved (must be "1") */
+ Ucbit res5_05 : 6; /* Reserved */
+ Ucbit uru : 1; /* Disk is for unrestricted use */
+ Ucbit res5_7 : 1; /* Reserved (must be "0") */
+ Ucbit a3_v : 1; /* A 3 Values valid */
+ Ucbit a2_v : 1; /* A 2 Values valid */
+ Ucbit a1_v : 1; /* A 1 Values valid */
+ Ucbit sub_type : 3; /* Disc sub type */
+ Ucbit erasable : 1; /* Disk is erasable */
+ Ucbit res6_7 : 1; /* Reserved (must be "1") */
+ Uchar lead_in[4]; /* Lead in time */
+ Uchar lead_out[4]; /* Lead out time */
+ Uchar res15; /* Reserved */
+ Ucbit clv_high : 4; /* Highes usable CLV recording speed */
+ Ucbit clv_low : 3; /* Lowest usable CLV recording speed */
+ Ucbit res16_7 : 1; /* Reserved (must be "0") */
+ Ucbit res17_0 : 1; /* Reserved */
+ Ucbit tgt_y_pow : 3; /* Tgt y val of the power mod fun */
+ Ucbit power_mult : 3; /* Power multiplication factor */
+ Ucbit res17_7 : 1; /* Reserved (must be "0") */
+ Ucbit res_18_30 : 4; /* Reserved */
+ Ucbit rerase_pwr_ratio: 3; /* Recommended erase/write power*/
+ Ucbit res18_7 : 1; /* Reserved (must be "1") */
+ Uchar res19; /* Reserved */
+ Uchar a2[3]; /* A 2 Values */
+ Uchar res23; /* Reserved */
+ Uchar a3[3]; /* A 3 Vaules */
+ Uchar res27; /* Reserved */
+};
+
+#else /* Motorola bitorder */
+
+struct atipdesc {
+ Ucbit res4_7 : 1; /* Reserved (must be "1") */
+ Ucbit ind_wr_power : 3; /* Indicative tgt writing power */
+ Ucbit res4_3 : 1; /* Reserved */
+ Ucbit ref_speed : 3; /* Reference speed */
+ Ucbit res5_7 : 1; /* Reserved (must be "0") */
+ Ucbit uru : 1; /* Disk is for unrestricted use */
+ Ucbit res5_05 : 6; /* Reserved */
+ Ucbit res6_7 : 1; /* Reserved (must be "1") */
+ Ucbit erasable : 1; /* Disk is erasable */
+ Ucbit sub_type : 3; /* Disc sub type */
+ Ucbit a1_v : 1; /* A 1 Values valid */
+ Ucbit a2_v : 1; /* A 2 Values valid */
+ Ucbit a3_v : 1; /* A 3 Values valid */
+ Uchar lead_in[4]; /* Lead in time */
+ Uchar lead_out[4]; /* Lead out time */
+ Uchar res15; /* Reserved */
+ Ucbit res16_7 : 1; /* Reserved (must be "0") */
+ Ucbit clv_low : 3; /* Lowest usable CLV recording speed */
+ Ucbit clv_high : 4; /* Highes usable CLV recording speed */
+ Ucbit res17_7 : 1; /* Reserved (must be "0") */
+ Ucbit power_mult : 3; /* Power multiplication factor */
+ Ucbit tgt_y_pow : 3; /* Tgt y val of the power mod fun */
+ Ucbit res17_0 : 1; /* Reserved */
+ Ucbit res18_7 : 1; /* Reserved (must be "1") */
+ Ucbit rerase_pwr_ratio: 3; /* Recommended erase/write power*/
+ Ucbit res_18_30 : 4; /* Reserved */
+ Uchar res19; /* Reserved */
+ Uchar a2[3]; /* A 2 Values */
+ Uchar res23; /* Reserved */
+ Uchar a3[3]; /* A 3 Vaules */
+ Uchar res27; /* Reserved */
+};
+
+#endif
+
+struct atipinfo {
+ struct tocheader hd;
+ struct atipdesc desc;
+};
+
+/*
+ * XXX Check how we may merge Track_info & Rzone_info
+ */
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct track_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar track_number; /* Track number for this info */
+ Uchar session_number; /* Session number for this info */
+ Uchar res4; /* Reserved */
+ Ucbit track_mode : 4; /* Track mode (Q-sub control) */
+ Ucbit copy : 1; /* This track is a higher copy */
+ Ucbit damage : 1; /* if 1 & nwa_valid 0: inc track*/
+ Ucbit res5_67 : 2; /* Reserved */
+ Ucbit data_mode : 4; /* Data mode of this track */
+ Ucbit fp : 1; /* This is a fixed packet track */
+ Ucbit packet : 1; /* This track is in packet mode */
+ Ucbit blank : 1; /* This is an invisible track */
+ Ucbit rt : 1; /* This is a reserved track */
+ Ucbit nwa_valid : 1; /* Next writable addr valid */
+ Ucbit res7_17 : 7; /* Reserved */
+ Uchar track_start[4]; /* Track start address */
+ Uchar next_writable_addr[4]; /* Next writable address */
+ Uchar free_blocks[4]; /* Free usr blocks in this track*/
+ Uchar packet_size[4]; /* Packet size if in fixed mode */
+ Uchar track_size[4]; /* # of user data blocks in trk */
+};
+
+#else /* Motorola bitorder */
+
+struct track_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar track_number; /* Track number for this info */
+ Uchar session_number; /* Session number for this info */
+ Uchar res4; /* Reserved */
+ Ucbit res5_67 : 2; /* Reserved */
+ Ucbit damage : 1; /* if 1 & nwa_valid 0: inc track*/
+ Ucbit copy : 1; /* This track is a higher copy */
+ Ucbit track_mode : 4; /* Track mode (Q-sub control) */
+ Ucbit rt : 1; /* This is a reserved track */
+ Ucbit blank : 1; /* This is an invisible track */
+ Ucbit packet : 1; /* This track is in packet mode */
+ Ucbit fp : 1; /* This is a fixed packet track */
+ Ucbit data_mode : 4; /* Data mode of this track */
+ Ucbit res7_17 : 7; /* Reserved */
+ Ucbit nwa_valid : 1; /* Next writable addr valid */
+ Uchar track_start[4]; /* Track start address */
+ Uchar next_writable_addr[4]; /* Next writable address */
+ Uchar free_blocks[4]; /* Free usr blocks in this track*/
+ Uchar packet_size[4]; /* Packet size if in fixed mode */
+ Uchar track_size[4]; /* # of user data blocks in trk */
+};
+
+#endif
+
+/*
+ * XXX Check how we may merge Track_info & Rzone_info
+ */
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct rzone_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar rzone_num_lsb; /* RZone number LSB */
+ Uchar border_num_lsb; /* Border number LSB */
+ Uchar res_4; /* Reserved */
+ Ucbit res5_04 : 5; /* Reserved */
+ Ucbit damage : 1; /* Damaged RZone */
+ Ucbit res5_67 : 2; /* Reserved */
+ Ucbit res6_04 : 5; /* Reserved */
+ Ucbit incremental : 1; /* RZone is to be written incremental */
+ Ucbit blank : 1; /* RZone is blank */
+ Ucbit rt : 1; /* RZone is reserved */
+ Ucbit nwa_v : 1; /* Next WR address is valid */
+ Ucbit lra_v : 1; /* Last rec address is valid */
+ Ucbit res7_27 : 6; /* Reserved */
+ Uchar rzone_start[4]; /* RZone start address */
+ Uchar next_recordable_addr[4]; /* Next recordable address */
+ Uchar free_blocks[4]; /* Free blocks in RZone */
+ Uchar block_factor[4]; /* # of sectors of disc acc unit */
+ Uchar rzone_size[4]; /* RZone size */
+ Uchar last_recorded_addr[4]; /* Last Recorded addr in RZone */
+ Uchar rzone_num_msb; /* RZone number MSB */
+ Uchar border_num_msb; /* Border number MSB */
+ Uchar res_34_35[2]; /* Reserved */
+};
+
+#else /* Motorola bitorder */
+
+struct rzone_info {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar rzone_num_lsb; /* RZone number LSB */
+ Uchar border_num_lsb; /* Border number LSB */
+ Uchar res_4; /* Reserved */
+ Ucbit res5_67 : 2; /* Reserved */
+ Ucbit damage : 1; /* Damaged RZone */
+ Ucbit res5_04 : 5; /* Reserved */
+ Ucbit rt : 1; /* RZone is reserved */
+ Ucbit blank : 1; /* RZone is blank */
+ Ucbit incremental : 1; /* RZone is to be written incremental */
+ Ucbit res6_04 : 5; /* Reserved */
+ Ucbit res7_27 : 6; /* Reserved */
+ Ucbit lra_v : 1; /* Last rec address is valid */
+ Ucbit nwa_v : 1; /* Next WR address is valid */
+ Uchar rzone_start[4]; /* RZone start address */
+ Uchar next_recordable_addr[4]; /* Next recordable address */
+ Uchar free_blocks[4]; /* Free blocks in RZone */
+ Uchar block_factor[4]; /* # of sectors of disc acc unit */
+ Uchar rzone_size[4]; /* RZone size */
+ Uchar last_recorded_addr[4]; /* Last Recorded addr in RZone */
+ Uchar rzone_num_msb; /* RZone number MSB */
+ Uchar border_num_msb; /* Border number MSB */
+ Uchar res_34_35[2]; /* Reserved */
+};
+
+#endif
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct dvd_structure_00 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Ucbit book_version : 4; /* DVD Book version */
+ Ucbit book_type : 4; /* DVD Book type */
+ Ucbit minimum_rate : 4; /* Minimum data rate (coded) */
+ Ucbit disc_size : 4; /* Disc size (coded) */
+ Ucbit layer_type : 4; /* Layer type */
+ Ucbit track_path : 1; /* 0 = parallel, 1 = opposit dir*/
+ Ucbit numlayers : 2; /* Number of Layers (0 == 1) */
+ Ucbit res2_7 : 1; /* Reserved */
+ Ucbit track_density : 4; /* Track density (coded) */
+ Ucbit linear_density : 4; /* Linear data density (coded) */
+ Uchar res8; /* Reserved */
+ Uchar phys_start[3]; /* Starting Physical sector # */
+ Uchar res12; /* Reserved */
+ Uchar phys_end[3]; /* End physical data sector # */
+ Uchar res16; /* Reserved */
+ Uchar end_layer0[3]; /* End sector # in layer */
+ Ucbit res20 : 7; /* Reserved */
+ Ucbit bca : 1; /* BCA flag bit */
+};
+
+#else /* Motorola bitorder */
+
+struct dvd_structure_00 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Ucbit book_type : 4; /* DVD Book type */
+ Ucbit book_version : 4; /* DVD Book version */
+ Ucbit disc_size : 4; /* Disc size (coded) */
+ Ucbit minimum_rate : 4; /* Minimum data rate (coded) */
+ Ucbit res2_7 : 1; /* Reserved */
+ Ucbit numlayers : 2; /* Number of Layers (0 == 1) */
+ Ucbit track_path : 1; /* 0 = parallel, 1 = opposit dir*/
+ Ucbit layer_type : 4; /* Layer type */
+ Ucbit linear_density : 4; /* Linear data density (coded) */
+ Ucbit track_density : 4; /* Track density (coded) */
+ Uchar res8; /* Reserved */
+ Uchar phys_start[3]; /* Starting Physical sector # */
+ Uchar res12; /* Reserved */
+ Uchar phys_end[3]; /* End physical data sector # */
+ Uchar res16; /* Reserved */
+ Uchar end_layer0[3]; /* End sector # in layer */
+ Ucbit bca : 1; /* BCA flag bit */
+ Ucbit res20 : 7; /* Reserved */
+};
+
+#endif
+
+struct dvd_structure_01 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar copyr_prot_type; /* Copyright prot system type */
+ Uchar region_mgt_info; /* Region management info */
+ Uchar res67[2]; /* Reserved */
+};
+
+struct dvd_structure_02 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar key_data[2048]; /* Disc Key data */
+};
+
+struct dvd_structure_03 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar bca_info[1]; /* BCA information (12-188 bytes)*/
+};
+
+struct dvd_structure_04 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar man_info[2048]; /* Disc manufacturing info */
+};
+
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+
+struct dvd_structure_05 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Ucbit res4_03 : 4; /* Reserved */
+ Ucbit cgms : 2; /* CGMS (see below) */
+ Ucbit res4_6 : 1; /* Reserved */
+ Ucbit cpm : 1; /* This is copyrighted material */
+ Uchar res57[3]; /* Reserved */
+};
+
+#else /* Motorola bitorder */
+
+struct dvd_structure_05 {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Ucbit cpm : 1; /* This is copyrighted material */
+ Ucbit res4_6 : 1; /* Reserved */
+ Ucbit cgms : 2; /* CGMS (see below) */
+ Ucbit res4_03 : 4; /* Reserved */
+ Uchar res57[3]; /* Reserved */
+};
+
+#endif
+
+#define CGMS_PERMITTED 0 /* Unlimited copy permitted */
+#define CGMS_RES 1 /* Reserved */
+#define CGMS_ONE_COPY 2 /* One copy permitted */
+#define CGMS_NO_COPY 3 /* No copy permitted */
+
+struct dvd_structure_0D {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar last_rma_sector[2]; /* Last recorded RMA sector # */
+ Uchar rmd_bytes[1]; /* Content of Record man area */
+};
+
+struct dvd_structure_0E {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar field_id; /* Field ID (1) */
+ Uchar application_code; /* Disc Application code */
+ Uchar phys_data; /* Disc Phisical Data */
+ Uchar last_recordable_addr[3]; /* Last addr of recordable area */
+ Uchar res_a[2]; /* Reserved */
+ Uchar field_id_2; /* Field ID (2) */
+ Uchar ind_wr_power; /* Recommended writing power */
+ Uchar ind_wavelength; /* Wavelength for ind_wr_power */
+ Uchar opt_wr_strategy[4]; /* Optimum write Strategy */
+ Uchar res_b[1]; /* Reserved */
+ Uchar field_id_3; /* Field ID (3) */
+ Uchar man_id[6]; /* Manufacturer ID */
+ Uchar res_m1; /* Reserved */
+ Uchar field_id_4; /* Field ID (4) */
+ Uchar man_id2[6]; /* Manufacturer ID */
+ Uchar res_m2; /* Reserved */
+};
+
+struct dvd_structure_0F {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar res45[2]; /* Reserved */
+ Uchar random[2]; /* Random number */
+ Uchar year[4]; /* Year (ascii) */
+ Uchar month[2]; /* Month (ascii) */
+ Uchar day[2]; /* Day (ascii) */
+ Uchar hour[2]; /* Hour (ascii) */
+ Uchar minute[2]; /* Minute (ascii) */
+ Uchar second[2]; /* Second (ascii) */
+};
+
+struct dvd_structure_0F_w {
+ Uchar data_len[2]; /* Data len without this info */
+ Uchar res23[2]; /* Reserved */
+ Uchar res45[2]; /* Reserved */
+ Uchar year[4]; /* Year (ascii) */
+ Uchar month[2]; /* Month (ascii) */
+ Uchar day[2]; /* Day (ascii) */
+ Uchar hour[2]; /* Hour (ascii) */
+ Uchar minute[2]; /* Minute (ascii) */
+ Uchar second[2]; /* Second (ascii) */
+};
+
+struct mmc_cue {
+ Uchar cs_ctladr; /* CTL/ADR for this track */
+ Uchar cs_tno; /* This track number */
+ Uchar cs_index; /* Index within this track */
+ Uchar cs_dataform; /* Data form */
+ Uchar cs_scms; /* Serial copy management */
+ Uchar cs_min; /* Absolute time minutes */
+ Uchar cs_sec; /* Absolute time seconds */
+ Uchar cs_frame; /* Absolute time frames */
+};
+
+struct mmc_performance_header {
+ Uchar p_datalen[4]; /* Performance Data length */
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+ Ucbit p_exept :1; /* Nominal vs. Exept. conditions*/
+ Ucbit p_write :1; /* Write vs. Read performance */
+ Ucbit p_res_4 :6; /* Reserved bits... */
+#else /* Motorola bitorder */
+ Ucbit p_res_4 :6; /* Reserved bits... */
+ Ucbit p_write :1; /* Write vs. Read performance */
+ Ucbit p_exept :1; /* Nominal vs. Exept. conditions*/
+#endif
+ Uchar p_res[3]; /* Reserved bytes */
+};
+
+
+struct mmc_performance { /* Type == 00 (nominal) */
+ Uchar start_lba[4]; /* Starting LBA */
+ Uchar start_perf[4]; /* Start Performance */
+ Uchar end_lba[4]; /* Ending LBA */
+ Uchar end_perf[4]; /* Ending Performance */
+};
+
+struct mmc_exceptions { /* Type == 00 (execptions) */
+ Uchar lba[4]; /* LBA */
+ Uchar time[2]; /* Time */
+};
+
+struct mmc_write_speed { /* Type == 00 (write speed) */
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+ Ucbit p_mrw :1; /* Suitable for mixed read/write*/
+ Ucbit p_exact :1; /* Speed count for whole media */
+ Ucbit p_rdd :1; /* Media rotational control */
+ Ucbit p_wrc :2; /* Write rotational control */
+ Ucbit p_res :3; /* Reserved bits... */
+#else /* Motorola bitorder */
+ Ucbit p_res :3; /* Reserved bits... */
+ Ucbit p_wrc :2; /* Write rotational control */
+ Ucbit p_rdd :1; /* Media rotational control */
+ Ucbit p_exact :1; /* Speed count for whole media */
+ Ucbit p_mrw :1; /* Suitable for mixed read/write*/
+#endif
+ Uchar res[3]; /* Reserved Bytes */
+ Uchar end_lba[4]; /* Ending LBA */
+ Uchar read_speed[4]; /* Read Speed */
+ Uchar write_speed[4]; /* Write Speed */
+};
+
+#define WRC_DEF_RC 0 /* Media default rotational control */
+#define WRC_CAV 1 /* CAV */
+
+
+struct mmc_streaming { /* Performance for set streaming*/
+#if defined(_BIT_FIELDS_LTOH) /* Intel bitorder */
+ Ucbit p_ra :1; /* Random Acess */
+ Ucbit p_exact :1; /* Set values exactly */
+ Ucbit p_rdd :1; /* Restore unit defaults */
+ Ucbit p_wrc :2; /* Write rotational control */
+ Ucbit p_res :3; /* Reserved bits... */
+#else /* Motorola bitorder */
+ Ucbit p_res :3; /* Reserved bits... */
+ Ucbit p_wrc :2; /* Write rotational control */
+ Ucbit p_rdd :1; /* Restore unit defaults */
+ Ucbit p_exact :1; /* Set values exactly */
+ Ucbit p_ra :1; /* Random Acess */
+#endif
+ Uchar res[3]; /* Reserved Bytes */
+ Uchar start_lba[4]; /* Starting LBA */
+ Uchar end_lba[4]; /* Ending LBA */
+ Uchar read_size[4]; /* Read Size */
+ Uchar read_time[4]; /* Read Time */
+ Uchar write_size[4]; /* Write Size */
+ Uchar write_time[4]; /* Write Time */
+};
+
+#endif /* _SCSIMMC_H */
diff --git a/wodim/sector.c b/wodim/sector.c
new file mode 100644
index 0000000..2c253d6
--- /dev/null
+++ b/wodim/sector.c
@@ -0,0 +1,263 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)sector.c 1.13 04/03/01 Copyright 2001-2004 J. Schilling */
+/*
+ * Functions needed to use libedc_ecc from cdrecord
+ *
+ * Copyright (c) 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <standard.h>
+#include <utypes.h>
+#include <timedefs.h>
+#include <schily.h>
+
+#include "wodim.h"
+#include "movesect.h"
+
+#ifdef HAVE_LIB_EDC_ECC
+
+
+#define LAYER2
+#define EDC_LAYER2
+#define ENCODER
+#define EDC_ENCODER
+#include <ecc.h>
+
+#ifdef DO8
+#define HAVE_NEW_LIB_EDC
+#endif
+
+int encspeed(BOOL be_verbose);
+void encsectors(track_t *trackp, Uchar *bp, int address, int nsecs);
+void scrsectors(track_t *trackp, Uchar *bp, int address, int nsecs);
+void encodesector(Uchar *sp, int sectype, int address);
+void fillsector(Uchar *sp, int sectype, int address);
+
+/*
+ * Sector types known by lib libedc_ecc:
+ */
+#ifdef __comment__
+ /* MMC */
+#define MODE_0 0 /* -> XX 12+4+2336 (12+4uuu von libedc) */
+#define MODE_1 1 /* -> 8 12+4+2048+288 (124+4uuu+288 von libedc)*/
+#define MODE_2 2 /* -> 9 12+4+2336 (12+4uuu von libedc) */
+#define MODE_2_FORM_1 3 /* -> 10/11 12+4+8+2048+280 (12+4hhhuuu+280 von libedc)*/
+#define MODE_2_FORM_2 4 /* -> 12 (eher 13!) 12+4+8+2324+4 (12+4hhhuuu+4 von libedc)*/
+#define AUDIO 5
+#define UNKNOWN 6
+#endif
+
+/*
+ * known sector types
+ */
+#ifndef EDC_MODE_0
+#define EDC_MODE_0 MODE_0
+#endif
+#ifndef EDC_MODE_1
+#define EDC_MODE_1 MODE_1
+#endif
+#ifndef EDC_MODE_2
+#define EDC_MODE_2 MODE_2
+#endif
+#ifndef EDC_MODE_2_FORM_1
+#define EDC_MODE_2_FORM_1 MODE_2_FORM_1
+#endif
+#ifndef EDC_MODE_2_FORM_2
+#define EDC_MODE_2_FORM_2 MODE_2_FORM_2
+#endif
+#ifndef EDC_AUDIO
+#define EDC_AUDIO AUDIO
+#endif
+#ifndef EDC_UNKNOWN
+#define EDC_UNKNOWN UNKNOWN
+#endif
+
+/*
+ * Compute max sector encoding speed
+ */
+int
+encspeed(BOOL be_verbose)
+{
+ track_t t[1];
+ Uchar sect[2352];
+ int i;
+ struct timeval tv;
+ struct timeval tv2;
+
+ t[0].sectype = ST_MODE_1;
+
+ /*
+ * Encoding speed is content dependant.
+ * Set up a known non-null pattern in the sector before; to make
+ * the result of this test independant of the current stack content.
+ */
+ for (i = 0; i < 2352; ) {
+ sect[i++] = 'J';
+ sect[i++] = 'S';
+ }
+ gettimeofday(&tv, (struct timezone *)0);
+ for (i = 0; i < 75000; i++) { /* Goes up to 1000x */
+ encsectors(t, sect, 12345, 1);
+ gettimeofday(&tv2, (struct timezone *)0);
+ if (tv2.tv_sec >= (tv.tv_sec+1) &&
+ tv2.tv_usec >= tv.tv_usec)
+ break;
+ }
+ if (be_verbose) {
+ printf("Encoding speed : %dx (%d sectors/s) for libedc from Heiko Eißfeldt\n",
+ (i+74)/75, i);
+ }
+ return ((i+74)/75);
+}
+
+/*
+ * Encode sectors according to trackp->sectype
+ */
+void
+encsectors(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ int sectype = trackp->sectype;
+
+ if ((sectype & ST_MODE_MASK) == ST_MODE_AUDIO)
+ return;
+
+ while (--nsecs >= 0) {
+ encodesector(bp, sectype, address);
+ address++;
+ bp += trackp->secsize;
+ }
+}
+
+
+#ifdef CLONE_WRITE
+
+#define IS_SECHDR(p) ((p)[0] == 0 && \
+ (p)[1] == 0xFF && (p)[2] == 0xFF && \
+ (p)[3] == 0xFF && (p)[4] == 0xFF && \
+ (p)[5] == 0xFF && (p)[6] == 0xFF && \
+ (p)[7] == 0xFF && (p)[8] == 0xFF && \
+ (p)[9] == 0xFF && (p)[10] == 0xFF && \
+ (p)[11] == 0)
+/*
+ * Scramble data sectors without coding (needed for clone writing)
+ */
+void
+scrsectors(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ /*
+ * In Clone write mode, we cannot expect that the sector type
+ * of a "track" which really is a file holding the whole disk
+ * is flagged with something that makes sense.
+ *
+ * For this reason, we check each sector if it's a data sector
+ * and needs scrambling.
+ */
+ while (--nsecs >= 0) {
+ if (IS_SECHDR(bp))
+ scramble_L2(bp);
+ bp += trackp->secsize;
+ }
+}
+#else
+void
+scrsectors(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ comerrno(EX_BAD, "Cannot write in clone RAW mode.\n");
+}
+#endif
+
+/*
+ * Encode one sector according to trackp->sectype
+ */
+void
+encodesector(Uchar *sp, int sectype, int address)
+{
+ if (address < -150)
+ address += 450150;
+ else
+ address += 150;
+#define _address address
+
+
+ switch (sectype & ST_MODE_MASK) {
+
+ case ST_MODE_0:
+ do_encode_L2(sp, EDC_MODE_0, _address);
+ break;
+
+ case ST_MODE_1:
+ do_encode_L2(sp, EDC_MODE_1, _address);
+ break;
+
+ case ST_MODE_2:
+ do_encode_L2(sp, EDC_MODE_2, _address);
+ break;
+
+ case ST_MODE_2_FORM_1:
+ sp[16+2] &= ~0x20; /* Form 1 sector */
+ sp[16+4+2] &= ~0x20; /* Form 1 sector 2nd copy */
+ /* FALLTHROUGH */
+
+ case ST_MODE_2_MIXED:
+ do_encode_L2(sp, EDC_MODE_2_FORM_1, _address);
+ break;
+
+ case ST_MODE_2_FORM_2:
+ sp[16+2] |= 0x20; /* Form 2 sector */
+ sp[16+4+2] |= 0x20; /* Form 2 sector 2nd copy */
+
+ do_encode_L2(sp, EDC_MODE_2_FORM_2, _address);
+ break;
+
+ case ST_MODE_AUDIO:
+ return;
+ default:
+ fill2352(sp, '\0');
+ return;
+ }
+ if ((sectype & ST_NOSCRAMBLE) == 0) {
+ scramble_L2(sp);
+#ifndef EDC_SCRAMBLE_NOSWAP
+ swabbytes(sp, 2352);
+#endif
+ }
+}
+
+/*
+ * Create one zero filles encoded sector (according to trackp->sectype)
+ */
+void
+fillsector(Uchar *sp, int sectype, int address)
+{
+ fill2352(sp, '\0');
+ encodesector(sp, sectype, address);
+}
+
+#endif /* HAVE_LIB_EDC_ECC */
diff --git a/wodim/subchan.c b/wodim/subchan.c
new file mode 100644
index 0000000..7195992
--- /dev/null
+++ b/wodim/subchan.c
@@ -0,0 +1,997 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)subchan.c 1.20 05/06/11 Copyright 2000-2004 J. Schilling */
+/*
+ * Subchannel processing
+ *
+ * Copyright (c) 2000-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <utypes.h>
+#include <schily.h>
+
+#include <usal/scsitransp.h>
+
+#include "wodim.h"
+#include "crc16.h"
+
+int do_leadin(track_t *trackp);
+int write_leadin(SCSI *usalp, cdr_t *dp, track_t *trackp, int leadinstart);
+int write_leadout(SCSI *usalp, cdr_t *dp, track_t *trackp);
+void fillsubch(track_t *trackp, Uchar *sp, int secno, int nsecs);
+void filltpoint(Uchar *sub, int ctrl_adr, int point, msf_t *mp);
+void fillttime(Uchar *sub, msf_t *mp);
+static void filldsubq(Uchar *sub, int ca, int t, int i, msf_t *mrp,
+ msf_t *mp);
+static void fillmcn(Uchar *sub, Uchar *mcn);
+static void fillisrc(Uchar *sub, Uchar *isrc);
+static int ascii2q(int c);
+static void qpto16(Uchar *sub, Uchar *subq, int dop);
+void qpto96(Uchar *sub, Uchar *subq, int dop);
+void addrw(Uchar *sub, Uchar *subrwptr);
+void qwto16(Uchar *subq, Uchar *subptr);
+void subrecodesecs(track_t *trackp, Uchar *bp, int address, int nsecs);
+static void subinterleave(Uchar *sub);
+
+/*#define TEST_CRC*/
+#ifdef TEST_CRC
+static void testcrc(void);
+#endif
+
+/*Die 96 Bits == 12 Bytes haben folgendes Aussehen:*/
+
+struct q {
+ Uchar ctrl_adr; /* 0 (ctrl << 4) | adr */
+ Uchar track; /* 1 current track # */
+ Uchar index; /* 2 current index # */
+ Uchar pmin; /* 3 Relative time minutes part */
+ Uchar psec; /* 4 Relative time seconds part */
+ Uchar pframe; /* 5 Relative time frames part */
+ Uchar zero; /* 6 */
+ Uchar amin; /* 7 Absolute time minutes part */
+ Uchar asec; /* 8 Absolute time seconds part */
+ Uchar aframe; /* 9 Absolute time frames part */
+ Uchar crc1; /* 10 all bits inverted. Polynom is */
+ Uchar crc2; /* 11 X^16 + X^12 + X^5 + 1 */
+};
+
+Uchar _subq[110][12];
+int _nsubh;
+
+extern int lverbose;
+extern int xdebug;
+
+/*
+ * Prepare master sunchannel data for RAW TOC.
+ */
+int
+do_leadin(track_t *trackp)
+{
+ int tracks = trackp->tracks;
+ msf_t m;
+ int ctrl;
+ int i;
+ int toctype = trackp[0].tracktype & TOC_MASK;
+
+ if (_nsubh) {
+ if (xdebug)
+ printf("Using CLONE TOC....\n");
+ return (0);
+ }
+ if (xdebug)
+ printf("Leadin TOC Type: %d\n", trackp[0].tracktype & TOC_MASK);
+ if (lverbose > 1) {
+ for (i = 1; i <= tracks+1; i++)
+ printf("Track %d start %ld\n", i, trackp[i].trackstart);
+ }
+
+#ifdef TEST_CRC
+ testcrc();
+/* exit(1);*/
+#endif
+
+ fillbytes(_subq, sizeof (_subq), '\0');
+
+ /*
+ * Fill in point 0xA0 for first track # on disk
+ */
+ ctrl = (st2mode[trackp[0].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[0]))
+ ctrl |= TM_ALLOW_COPY << 4;
+ m.msf_min = trackp[1].trackno;
+ /*
+ * Disk Type: 0 = AUDIO/DATA, 0x10 = CDI, 0x20 = XA mode 2
+ */
+ m.msf_sec = toc2sess[toctype & TOC_MASK];
+ m.msf_sec = from_bcd(m.msf_sec); /* convert to BCD */
+ m.msf_frame = 0;
+ filltpoint(_subq[0], ctrl|0x01, 0xA0, &m);
+ if (lverbose > 2)
+ usal_prbytes("", _subq[0], 12);
+
+ /*
+ * Fill in point 0xA1 for last track # on disk
+ */
+ ctrl = (st2mode[trackp[tracks].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[tracks]))
+ ctrl |= TM_ALLOW_COPY << 4;
+ m.msf_min = trackp[tracks].trackno;
+ m.msf_sec = 0;
+ m.msf_frame = 0;
+ filltpoint(_subq[1], ctrl|0x01, 0xA1, &m);
+ if (lverbose > 2)
+ usal_prbytes("", _subq[1], 12);
+
+ /*
+ * Fill in point 0xA2 for lead out start time on disk
+ */
+ lba_to_msf(trackp[tracks+1].trackstart, &m);
+ ctrl = (st2mode[trackp[tracks].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[tracks]))
+ ctrl |= TM_ALLOW_COPY << 4;
+ filltpoint(_subq[2], ctrl|0x01, 0xA2, &m);
+ if (lverbose > 2)
+ usal_prbytes("", _subq[2], 12);
+
+ /*
+ * Fill in track start times.
+ */
+ for (i = 1; i <= tracks; i++) {
+ lba_to_msf(trackp[i].trackstart, &m);
+ ctrl = (st2mode[trackp[i].sectype & ST_MASK]) << 4;
+ if (is_copy(&trackp[i]))
+ ctrl |= TM_ALLOW_COPY << 4;
+ filltpoint(_subq[i-1+3], ctrl|0x01, to_bcd(trackp[i].trackno), &m); /* track n */
+ if (lverbose > 2)
+ usal_prbytes("", _subq[i-1+3], 12);
+ }
+ return (0);
+}
+
+/*
+ * Write TOC (lead-in)
+ *
+ * Use previously prepared master subchannel data to create the
+ * subchannel frames for the lead-in.
+ */
+int
+write_leadin(SCSI *usalp, cdr_t *dp, track_t *trackp, int leadinstart)
+{
+ msf_t m;
+ int i;
+ Uint j;
+ Uchar *bp = usalp->bufptr;
+ Uchar *subp;
+ Uchar *sp;
+ int secsize;
+ int secspt;
+ int bytespt;
+ long amount;
+ int startsec;
+ long bytes = 0L;
+ int textoff = 0;
+ msf_t msf;
+
+ secsize = trackp[0].secsize;
+ secspt = trackp[0].secspt;
+ bytespt = secspt * secsize;
+
+ lba_to_msf(leadinstart, &msf);
+
+ fillbytes(bp, bytespt, '\0');
+
+ if (_nsubh) {
+ if (xdebug)
+ printf("Using CLONE LEADIN\n");
+ }
+ if (xdebug) {
+ printf("Leadinstart: %d %d:%d/%d",
+ leadinstart,
+ msf.msf_min, msf.msf_sec, msf.msf_frame);
+ printf(" FLAGS: 0x%X sect: %X RAW16:%d secs: %d spt: %d\n",
+ trackp[0].flags, trackp[0].sectype,
+ is_raw16(&trackp[0]), secsize, secspt);
+ }
+
+ startsec = leadinstart;
+ sp = bp;
+ subp = bp + 2352;
+ for (i = leadinstart, j = 0; i < -150; i++, j++) {
+ /*
+ * TOC hat folgende unterschiedliche Sub Q Frames:
+ * A0 First Track
+ * A1 Last Track
+ * A3 Lead out start
+ * 1..99 Tracks
+ * == 3 + N* tracks
+ * Jeder Frame wird 3x wiederholt.
+ */
+ if (_nsubh) {
+ if (j >= (3*_nsubh))
+ j = 0;
+ } else {
+ if (j >= (3*3 + 3*trackp->tracks))
+ j = 0;
+ }
+ lba_to_msf((long)i, &m);
+ fillttime(_subq[j/3], &m);
+ fillcrc(_subq[j/3], 12);
+ if (xdebug > 2)
+ usal_prbytes("", _subq[j/3], 12);
+ if (is_raw16(&trackp[0])) {
+ qpto16(subp, _subq[j/3], 0);
+ } else {
+ extern Uchar *textsub;
+ extern int textlen;
+
+ qpto96(subp, _subq[j/3], 0);
+ if (textsub) {
+ addrw(subp, &textsub[textoff]);
+ textoff += 96;
+ if (textoff >= textlen)
+ textoff = 0;
+ }
+/* if (is_raw96p(&trackp[0]))*/
+/* subinterleave(subp);*/
+ }
+ if ((startsec+secspt-1) == i || i == -151) {
+ if ((i-startsec+1) < secspt) {
+ secspt = i-startsec+1;
+ bytespt = secspt * secsize;
+ }
+ encsectors(trackp, bp, startsec, secspt);
+
+ amount = write_secs(usalp, dp,
+ (char *)bp, startsec, bytespt, secspt, FALSE);
+ if (amount < 0) {
+ printf("write leadin data: error after %ld bytes\n",
+ bytes);
+ return (-1);
+ }
+ bytes += amount;
+ startsec = i+1;
+ sp = bp;
+ subp = bp + 2352;
+ continue;
+ }
+ sp += secsize;
+ subp += secsize;
+ }
+ return (0);
+}
+
+/*
+ * Write Track 0xAA (lead-out)
+ */
+int
+write_leadout(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int tracks = trackp->tracks;
+ msf_t m;
+ msf_t mr;
+ int ctrl;
+ int i;
+ int j;
+ Uchar *bp = usalp->bufptr;
+ Uchar *subp;
+ Uchar *sp;
+ int secsize;
+ int secspt;
+ int bytespt;
+ long amount;
+ long startsec;
+ long endsec;
+ long bytes = 0L;
+ int leadoutstart;
+ Uchar sub[12];
+ BOOL p;
+ msf_t msf;
+
+ fillbytes(sub, 12, '\0');
+
+ secsize = trackp[tracks+1].secsize;
+ secspt = trackp[tracks+1].secspt;
+ bytespt = secspt * secsize;
+
+ leadoutstart = trackp[tracks+1].trackstart;
+ lba_to_msf(leadoutstart, &msf);
+
+ fillbytes(bp, bytespt, '\0');
+
+ if (xdebug) {
+ printf("Leadoutstart: %d %d:%d/%d amt %ld",
+ leadoutstart,
+ msf.msf_min, msf.msf_sec, msf.msf_frame,
+ trackp[tracks+1].tracksecs);
+ printf(" FLAGS: 0x%X sect: %X RAW16:%d secs: %d spt: %d\n",
+ trackp[tracks+1].flags, trackp[tracks+1].sectype,
+ is_raw16(&trackp[tracks+1]), secsize, secspt);
+ }
+
+ startsec = leadoutstart;
+ endsec = startsec + trackp[tracks+1].tracksecs;
+ sp = bp;
+ subp = bp + 2352;
+ ctrl = (st2mode[trackp->sectype & ST_MASK]) << 4;
+ if (is_copy(trackp))
+ ctrl |= TM_ALLOW_COPY << 4;
+
+ for (i = leadoutstart, j = 0; i < endsec; i++, j++) {
+
+ lba_to_msf((long)i, &m);
+ sec_to_msf((long)j, &mr);
+ filldsubq(sub, ctrl|0x01, 0xAA, 1, &mr, &m);
+ sub[1] = 0xAA;
+ fillcrc(sub, 12);
+ p = (j % 150) < 75;
+ if (j < 150)
+ p = FALSE;
+ if (xdebug > 2)
+ usal_prbytes(p?"P":" ", sub, 12);
+
+ if (is_raw16(&trackp[0])) {
+ qpto16(subp, sub, p);
+ } else {
+ qpto96(subp, sub, p);
+/* if (is_raw96p(&trackp[0]))*/
+/* subinterleave(subp);*/
+ }
+ if ((startsec+secspt-1) == i || i == (endsec-1)) {
+ if ((i-startsec+1) < secspt) {
+ secspt = i-startsec+1;
+ bytespt = secspt * secsize;
+ }
+ encsectors(trackp, bp, startsec, secspt);
+
+ amount = write_secs(usalp, dp,
+ (char *)bp, startsec, bytespt, secspt, FALSE);
+ if (amount < 0) {
+ printf("write leadout data: error after %ld bytes\n",
+ bytes);
+ return (-1);
+ }
+ bytes += amount;
+ startsec = i+1;
+ sp = bp;
+ subp = bp + 2352;
+ continue;
+ }
+ sp += secsize;
+ subp += secsize;
+ }
+ return (0);
+}
+
+/*
+ * Fill in subchannel data.
+ *
+ * This function is used to prepare the sub channels when writing
+ * the data part of a CD (bewteen lead-in and lead-out).
+ */
+void
+fillsubch(track_t *trackp,
+ Uchar *sp /* Sector data pointer */,
+ int secno /* Starting sector # */,
+ int nsecs /* # of sectors to fill */)
+{
+ msf_t m;
+ msf_t mr;
+ int ctrl;
+ int i;
+ int rsecno;
+ int end;
+ int secsize = trackp->secsize;
+ int trackno = trackp->trackno;
+ int nindex = trackp->nindex;
+ int curindex;
+ long *tindex = NULL;
+ long nextindex = 0L;
+ Uchar sub[12];
+ Uchar *sup;
+ char *mcn;
+ /*
+ * In theory this should make fillsubch() non-reenrtrant but it seems
+ * that the probability check at the beginning of the function is
+ * sufficient to make it work as expected.
+ */
+static long nextmcn = -1000000L;
+static long nextisrc = -1000000L;
+static Uchar lastindex = 255;
+
+ fillbytes(sub, 12, '\0');
+
+ mcn = track_base(trackp)->isrc;
+ rsecno = secno - trackp->trackstart;
+ if ((secno + nsecs) > (trackp->trackstart + trackp->tracksecs)) {
+ comerrno(EX_BAD, "Implementation botch: track boundary in buffer.\n");
+ }
+ sup = sp + 2352;
+ if (mcn && (nextmcn < secno || nextmcn > (secno+100))) {
+ nextmcn = secno/100*100 + 99;
+ }
+ if (trackp->isrc && (nextisrc < secno || nextisrc > (secno+100))) {
+ nextisrc = secno/100*100 + 49;
+ }
+ ctrl = (st2mode[trackp->sectype & ST_MASK]) << 4;
+ if (is_copy(trackp))
+ ctrl |= TM_ALLOW_COPY << 4;
+
+#ifdef SUB_DEBUG
+ fprintf(stderr, "Tracknl %d nindex %d trackstart %ld rsecno %d index0start %ld nsecs %d\n",
+ trackno, nindex, trackp->trackstart, rsecno, trackp->index0start, nsecs);
+#endif
+
+ if (rsecno < 0) {
+ /*
+ * Called to manually write pregap null data into the pregap
+ * while 'trackp' points to the curent track. For this reason,
+ * the sectors are before the start of track 'n' index 0.
+ */
+ curindex = 0;
+ end = trackp->trackstart;
+
+ } else if (rsecno > trackp->index0start) {
+ /*
+ * This track contains pregap of next track.
+ * We currently are inside this pregap.
+ */
+ trackno++;
+ curindex = 0;
+ end = trackp->trackstart + trackp->tracksecs;
+ } else {
+ /*
+ * We are inside the normal data part of this track.
+ * This is index_1...index_m for track n.
+ * Set 'end' to 0 in this case although it is only used
+ * if 'index' is 0. But GCC gives a warning that 'end'
+ * might be uninitialized.
+ */
+ end = 0;
+ curindex = 1;
+ if (nindex > 1) {
+ tindex = trackp->tindex;
+ nextindex = trackp->tracksecs;
+ /*
+ * find current index in list
+ */
+ for (curindex = nindex; curindex >= 1; curindex--) {
+ if (rsecno >= tindex[curindex]) {
+ if (curindex < nindex)
+ nextindex = tindex[curindex+1];
+ break;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < nsecs; i++) {
+
+ if (tindex != NULL && rsecno >= nextindex) {
+ /*
+ * Skip to next index in list.
+ */
+ if (curindex < nindex) {
+ curindex++;
+ nextindex = tindex[curindex+1];
+ }
+ }
+ if (rsecno == trackp->index0start) {
+ /*
+ * This track contains pregap of next track.
+ */
+ trackno++;
+ curindex = 0;
+ end = trackp->trackstart + trackp->tracksecs;
+ }
+ lba_to_msf((long)secno, &m);
+ if (curindex == 0)
+ sec_to_msf((long)end-1 - secno, &mr);
+ else
+ sec_to_msf((long)rsecno, &mr);
+ if (is_scms(trackp)) {
+ if ((secno % 8) <= 3) {
+ ctrl &= ~(TM_ALLOW_COPY << 4);
+ } else {
+ ctrl |= TM_ALLOW_COPY << 4;
+ }
+ }
+ filldsubq(sub, ctrl|0x01, trackno, curindex, &mr, &m);
+ if (mcn && (secno == nextmcn)) {
+ if (curindex == lastindex) {
+ fillmcn(sub, (Uchar *)mcn);
+ nextmcn = (secno+1)/100*100 + 99;
+ } else {
+ nextmcn++;
+ }
+ }
+ if (trackp->isrc && (secno == nextisrc)) {
+ if (curindex == lastindex) {
+ fillisrc(sub, (Uchar *)trackp->isrc);
+ nextisrc = (secno+1)/100*100 + 49;
+ } else {
+ nextisrc++;
+ }
+ }
+ fillcrc(sub, 12);
+ if (xdebug > 2)
+ usal_prbytes(curindex == 0 ? "P":" ", sub, 12);
+ if (is_raw16(trackp)) {
+ qpto16(sup, sub, curindex == 0);
+ } else {
+ qpto96(sup, sub, curindex == 0);
+/* if (is_raw96p(trackp))*/
+/* subinterleave(sup);*/
+ }
+ lastindex = curindex;
+ secno++;
+ rsecno++;
+ sup += secsize;
+ }
+}
+
+
+/*
+ * Fill TOC Point
+ * Ax Werte einfüllen.
+ */
+void
+filltpoint(Uchar *sub, int ctrl_adr, int point, msf_t *mp)
+{
+ sub[0] = ctrl_adr;
+ sub[2] = point;
+ sub[7] = to_bcd(mp->msf_min);
+ sub[8] = to_bcd(mp->msf_sec);
+ sub[9] = to_bcd(mp->msf_frame);
+}
+
+/*
+ * Fill TOC time
+ * Aktuelle Zeit in TOC Sub-Q einfüllen.
+ */
+void
+fillttime(Uchar *sub, msf_t *mp)
+{
+ sub[3] = to_bcd(mp->msf_min);
+ sub[4] = to_bcd(mp->msf_sec);
+ sub[5] = to_bcd(mp->msf_frame);
+}
+
+/*
+ * Q-Sub in Datenbereich füllen.
+ */
+static void
+filldsubq(Uchar *sub, int ca, int t, int i, msf_t *mrp, msf_t *mp)
+{
+ sub[0] = ca;
+ sub[1] = to_bcd(t);
+ sub[2] = to_bcd(i);
+ sub[3] = to_bcd(mrp->msf_min);
+ sub[4] = to_bcd(mrp->msf_sec);
+ sub[5] = to_bcd(mrp->msf_frame);
+
+ sub[7] = to_bcd(mp->msf_min);
+ sub[8] = to_bcd(mp->msf_sec);
+ sub[9] = to_bcd(mp->msf_frame);
+}
+
+/*
+ * Fill MCN
+ * MCN konvertieren und in Sub-Q einfüllen.
+ */
+static void
+fillmcn(Uchar *sub, Uchar *mcn)
+{
+ register int i;
+ register int c;
+
+ sub[0] = ADR_MCN;
+ for (i = 1; i <= 8; i++) {
+ c = *mcn++;
+ if (c >= '0' && c <= '9')
+ sub[i] = (c - '0') << 4;
+ else
+ sub[i] = 0;
+
+ if (c != '\0')
+ c = *mcn++;
+ if (c >= '0' && c <= '9')
+ sub[i] |= (c - '0');
+
+ if (c == '\0') {
+ i++;
+ break;
+ }
+ }
+ for (; i <= 8; i++)
+ sub[i] = '\0';
+}
+
+/*
+ * Fill ISRC
+ * ISRC konvertieren und in Sub-Q einfüllen.
+ */
+static void
+fillisrc(Uchar *sub, Uchar *isrc)
+{
+ register int i;
+ register int j;
+ Uchar tmp[13];
+ Uchar *sp;
+
+ sub[0] = ADR_ISRC;
+ sp = &sub[1];
+
+ /*
+ * Convert into Sub-Q character coding
+ */
+ for (i = 0, j = 0; i < 12; i++) {
+ if (isrc[i+j] == '-')
+ j++;
+ if (isrc[i+j] == '\0')
+ break;
+ tmp[i] = ascii2q(isrc[i+j]);
+ }
+ for (; i < 13; i++)
+ tmp[i] = '\0';
+
+ /*
+ * The first 5 chars from e.g. "FI-BAR-99-00312"
+ */
+ sp[0] = tmp[0] << 2;
+ sp[0] |= (tmp[1] >> 4) & 0x03;
+ sp[1] = (tmp[1] << 4) & 0xF0;
+ sp[1] |= (tmp[2] >> 2) & 0x0F;
+ sp[2] = (tmp[2] << 6) & 0xC0;
+ sp[2] |= tmp[3] & 0x3F;
+ sp[3] = tmp[4] << 2;
+
+ /*
+ * Now 7 digits from e.g. "FI-BAR-99-00312"
+ */
+ for (i = 4, j = 5; i < 8; i++) {
+ sp[i] = tmp[j++] << 4;
+ sp[i] |= tmp[j++];
+ }
+}
+
+/*
+ * ASCII -> Sub-Q ISRC code conversion
+ */
+static int
+ascii2q(int c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ else if (c >= '@' && c <= 'o')
+ return (0x10 + c - '@');
+ return (0);
+}
+
+/*
+ * Q-Sub auf 16 Bytes blähen und P-Sub addieren
+ *
+ * OUT: sub, IN: subqptr
+ */
+static void
+qpto16(Uchar *sub, Uchar *subqptr, int dop)
+{
+ if (sub != subqptr)
+ movebytes(subqptr, sub, 12);
+ sub[12] = '\0';
+ sub[13] = '\0';
+ sub[14] = '\0';
+ sub[15] = '\0';
+ if (dop)
+ sub[15] |= 0x80;
+
+}
+
+/*
+ * Q-Sub auf 96 Bytes blähen und P-Sub addieren
+ *
+ * OUT: sub, IN: subqptr
+ */
+void
+qpto96(Uchar *sub, Uchar *subqptr, int dop)
+{
+ Uchar tmp[16];
+ Uchar *p;
+ int c;
+ int i;
+
+ if (subqptr == sub) {
+ /*
+ * Remember 12 byte subchannel data if subchannel
+ * is overlapping.
+ */
+ movebytes(subqptr, tmp, 12);
+ subqptr = tmp;
+ }
+ /*
+ * Clear all subchannel bits in the 96 byte target space.
+ */
+ fillbytes(sub, 96, '\0');
+
+ /* BEGIN CSTYLED */
+ if (dop) for (i = 0, p = sub; i < 96; i++) {
+ *p++ |= 0x80;
+ }
+ for (i = 0, p = sub; i < 12; i++) {
+ c = subqptr[i] & 0xFF;
+/*printf("%02X\n", c);*/
+ if (c & 0x80)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x40)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x20)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x10)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x08)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x04)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x02)
+ *p++ |= 0x40;
+ else
+ p++;
+ if (c & 0x01)
+ *p++ |= 0x40;
+ else
+ p++;
+ }
+}
+
+/*
+ * Add R-W-Sub (96 Bytes) to P-Q-Sub (96 Bytes)
+ *
+ * OUT: sub, IN: subrwptr
+ */
+void
+addrw(register Uchar *sub, register Uchar *subrwptr)
+{
+ register int i;
+
+#define DO8(a) a; a; a; a; a; a; a; a;
+
+ for (i = 0; i < 12; i++) {
+ DO8(*sub++ |= *subrwptr++ & 0x3F);
+ }
+}
+
+/*
+ * Q-W-Sub (96 Bytes) auf 16 Bytes schrumpfen
+ *
+ * OUT: subq, IN: subptr
+ */
+void
+qwto16(Uchar *subq, Uchar *subptr)
+{
+ register int i;
+ register int np = 0;
+ register Uchar *p;
+ Uchar tmp[96];
+
+ p = subptr;
+ for (i = 0; i < 96; i++)
+ if (*p++ & 0x80)
+ np++;
+ p = subptr;
+ if (subptr == subq) {
+ /*
+ * Remember 96 byte subchannel data if subchannel
+ * is overlapping.
+ */
+ movebytes(subptr, tmp, 96);
+ p = tmp;
+ }
+
+ for (i = 0; i < 12; i++) {
+ subq[i] = 0;
+ if (*p++ & 0x40)
+ subq[i] |= 0x80;
+ if (*p++ & 0x40)
+ subq[i] |= 0x40;
+ if (*p++ & 0x40)
+ subq[i] |= 0x20;
+ if (*p++ & 0x40)
+ subq[i] |= 0x10;
+ if (*p++ & 0x40)
+ subq[i] |= 0x08;
+ if (*p++ & 0x40)
+ subq[i] |= 0x04;
+ if (*p++ & 0x40)
+ subq[i] |= 0x02;
+ if (*p++ & 0x40)
+ subq[i] |= 0x01;
+ }
+ subq[12] = 0;
+ subq[13] = 0;
+ subq[14] = 0;
+ if (np > (96/2))
+ subq[15] = 0x80;
+}
+
+/*
+ * Recode subchannels of sectors from 2352 + 96 bytes to 2352 + 16 bytes
+ */
+void
+subrecodesecs(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ bp += 2352;
+ while (--nsecs >= 0) {
+ qwto16(bp, bp);
+ bp += trackp->isecsize;
+ }
+}
+
+#ifndef HAVE_LIB_EDC_ECC
+void
+encsectors(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ int sectype = trackp->sectype;
+
+ if ((sectype & ST_MODE_MASK) == ST_MODE_AUDIO)
+ return;
+
+ comerrno(EX_BAD, "Can only write audio sectors in RAW mode.\n");
+}
+
+void
+scrsectors(track_t *trackp, Uchar *bp, int address, int nsecs)
+{
+ comerrno(EX_BAD, "Cannot write in clone RAW mode.\n");
+}
+#endif
+
+/*--------------------------------------------------------------------------*/
+#ifdef TEST_CRC
+
+Uchar tq[12] = { 0x01, 0x00, 0xA0, 0x98, 0x06, 0x12, 0x00, 0x01, 0x00, 0x00, 0xE3, 0x74 };
+
+/*
+01 00 A0 98 06 12 00 01 00 00 E3 74
+01 00 A0 98 06 13 00 01 00 00 49 25
+01 00 A1 98 06 14 00 13 00 00 44 21
+01 00 A1 98 06 15 00 13 00 00 EE 70
+01 00 A1 98 06 16 00 13 00 00 00 A2
+01 00 A2 98 06 17 00 70 40 73 E3 85
+01 00 A2 98 06 18 00 70 40 73 86 7C
+01 00 A2 98 06 19 00 70 40 73 2C 2D
+01 00 01 98 06 20 00 00 02 00 3B 71
+01 00 01 98 06 21 00 00 02 00 91 20
+01 00 01 98 06 22 00 00 02 00 7F F2
+01 00 02 98 06 23 00 03 48 45 BE E0
+01 00 02 98 06 24 00 03 48 45 D9 34
+
+*/
+
+static int b(int bcd);
+
+
+static int
+b(int bcd)
+{
+ return ((bcd & 0x0F) + 10 * (((bcd)>> 4) & 0x0F));
+}
+
+static void
+testcrc()
+{
+ struct q q;
+ int ocrc;
+ int crc1;
+ int crc2;
+
+ movebytes(&tq, &q, 12);
+ crc1 = q.crc1 & 0xFF;
+ crc2 = q.crc2 & 0xFF;
+
+ /*
+ * Per RED Book, CRC Bits on disk are inverted.
+ * Invert them again to make calcCRC() work.
+ */
+ q.crc1 ^= 0xFF;
+ q.crc2 ^= 0xFF;
+
+ ocrc = calcCRC((Uchar *)&q, 12);
+ printf("AC: %02X t: %3d (%02X) i: %3d (%02X) %d:%d:%d %d:%d:%d %02X%02X %X (%X)\n",
+ q.ctrl_adr,
+ b(q.track),
+ b(q.track) & 0xFF,
+ b(q.index),
+ q.index & 0xFF,
+ b(q.pmin),
+ b(q.psec),
+ b(q.pframe),
+ b(q.amin),
+ b(q.asec),
+ b(q.aframe),
+ crc1, crc2,
+ ocrc,
+ fillcrc((Uchar *)&q, 12) & 0xFFFF);
+}
+#endif /* TEST_CRC */
+
+#ifdef sss
+96 / 24 = 4
+
+index 1 < - > 18
+index 2 < - > 5
+index 3 < - > 23
+
+delay index mod 8
+#endif
+
+/*
+ * Sub 96 Bytes Interleave
+ */
+static void
+subinterleave(Uchar *sub)
+{
+ Uchar *p;
+ int i;
+
+ for (i = 0, p = sub; i < 4; i++) {
+ Uchar save;
+
+ /*
+ * index 1 < - > 18
+ * index 2 < - > 5
+ * index 3 < - > 23
+ */
+ save = p[1];
+ p[1] = p[18];
+ p[18] = save;
+
+ save = p[2];
+ p[2] = p[5];
+ p[5] = save;
+
+ save = p[3];
+ p[3] = p[23];
+ p[23] = save;
+
+ p += 24;
+ }
+}
diff --git a/wodim/wm_packet.c b/wodim/wm_packet.c
new file mode 100644
index 0000000..9566a52
--- /dev/null
+++ b/wodim/wm_packet.c
@@ -0,0 +1,306 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)wm_packet.c 1.25 04/03/01 Copyright 1995, 1997, 2001-2004 J. Schilling */
+/*
+ * CDR write method abtraction layer
+ * packet writing intercace routines
+ *
+ * Copyright (c) 1995, 1997, 2001-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <timedefs.h>
+#include <standard.h>
+#include <utypes.h>
+#include <schily.h>
+
+#include <usal/scsitransp.h>
+#include "wodim.h"
+#include "xio.h"
+
+extern int debug;
+extern int verbose;
+extern int lverbose;
+
+extern char *buf; /* The transfer buffer */
+
+int write_packet_data(SCSI *usalp, cdr_t *dp, track_t *trackp);
+
+int
+write_packet_data(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int track = trackp->trackno;
+ int f = -1;
+ int isaudio;
+ long startsec;
+ Llong bytes_read = 0;
+ Llong bytes = 0;
+ Llong savbytes = 0;
+ int count;
+ Llong tracksize;
+ int secsize;
+ int secspt;
+ int bytespt;
+ long amount;
+ int pad;
+ int retried;
+ long nextblock;
+ int bytes_to_read;
+ BOOL neednl = FALSE;
+ BOOL islast = FALSE;
+ char *bp = buf;
+ struct timeval tlast;
+ struct timeval tcur;
+ float secsps = 75.0;
+long bsize;
+long bfree;
+#define BCAP
+#ifdef BCAP
+int per;
+#ifdef XBCAP
+int oper = -1;
+#endif
+#endif
+
+ if (dp->cdr_dstat->ds_flags & DSF_DVD)
+ secsps = 676.27;
+
+ usalp->silent++;
+ if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0)
+ bsize = -1L;
+ if (bsize == 0) /* If we have no (known) buffer, we cannot */
+ bsize = -1L; /* retrieve the buffer fill ratio */
+ else
+ dp->cdr_dstat->ds_buflow = 0;
+ usalp->silent--;
+
+ if (trackp->xfp != NULL)
+ f = xfileno(trackp->xfp);
+
+ isaudio = is_audio(trackp);
+ tracksize = trackp->tracksize;
+ startsec = trackp->trackstart;
+
+ secsize = trackp->secsize;
+ secspt = trackp->secspt;
+ bytespt = secsize * secspt;
+
+ pad = !isaudio && is_pad(trackp); /* Pad only data tracks */
+
+ if (debug) {
+ printf("secsize:%d secspt:%d bytespt:%d audio:%d pad:%d\n",
+ secsize, secspt, bytespt, isaudio, pad);
+ }
+
+ if (lverbose) {
+ if (tracksize > 0)
+ printf("\rTrack %02d: 0 of %4lld MB written.",
+ track, tracksize >> 20);
+ else
+ printf("\rTrack %02d: 0 MB written.", track);
+ flush();
+ neednl = TRUE;
+ }
+
+ gettimeofday(&tlast, (struct timezone *)0);
+ do {
+ bytes_to_read = bytespt;
+ if (tracksize > 0) {
+ if ((tracksize - bytes_read) > bytespt)
+ bytes_to_read = bytespt;
+ else
+ bytes_to_read = tracksize - bytes_read;
+ }
+ /* XXX next wr addr ??? */
+ count = get_buf(f, trackp, startsec, &bp, bytes_to_read);
+ if (count < 0)
+ comerr("read error on input file\n");
+ if (count == 0)
+ break;
+ bytes_read += count;
+ if (tracksize >= 0 && bytes_read >= tracksize) {
+ count -= bytes_read - tracksize;
+ if (trackp->padsecs == 0 || (bytes_read/secsize) >= 300)
+ islast = TRUE;
+ }
+
+ if (count < bytespt) {
+ if (debug) {
+ printf("\nNOTICE: reducing block size for last record.\n");
+ neednl = FALSE;
+ }
+
+ if ((amount = count % secsize) != 0) {
+ amount = secsize - amount;
+ fillbytes(&bp[count], amount, '\0');
+ count += amount;
+ printf("\nWARNING: padding up to secsize.\n");
+ neednl = FALSE;
+ }
+ if (is_packet(trackp) && trackp->pktsize > 0) {
+ if (count < bytespt) {
+ amount = bytespt - count;
+ count += amount;
+ printf("\nWARNING: padding remainder of packet.\n");
+ neednl = FALSE;
+ }
+ }
+ bytespt = count;
+ secspt = count / secsize;
+ if (trackp->padsecs == 0 || (bytes_read/secsize) >= 300)
+ islast = TRUE;
+ }
+
+ retried = 0;
+ retry:
+ /* XXX Fixed-packet writes can be very slow*/
+ if (is_packet(trackp) && trackp->pktsize > 0)
+ usal_settimeout(usalp, 100);
+ /* XXX */
+ if (is_packet(trackp) && trackp->pktsize == 0) {
+ if ((*dp->cdr_next_wr_address)(usalp, trackp, &nextblock) == 0) {
+ /*
+ * Some drives (e.g. Ricoh MPS6201S) do not
+ * increment the Next Writable Address value to
+ * point to the beginning of a new packet if
+ * their write buffer has underflowed.
+ */
+ if (retried && nextblock == startsec) {
+ startsec += 7;
+ } else {
+ startsec = nextblock;
+ }
+ }
+ }
+
+ amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast);
+ if (amount < 0) {
+ if (is_packet(trackp) && trackp->pktsize == 0 && !retried) {
+ printf("%swrite track data: error after %lld bytes, retry with new packet\n",
+ neednl?"\n":"", bytes);
+ retried = 1;
+ neednl = FALSE;
+ goto retry;
+ }
+ printf("%swrite track data: error after %lld bytes\n",
+ neednl?"\n":"", bytes);
+ return (-1);
+ }
+ bytes += amount;
+ startsec += amount / secsize;
+
+ if (lverbose && (bytes >= (savbytes + 0x100000))) {
+ int fper;
+ int nsecs = (bytes - savbytes) / secsize;
+ float fspeed;
+
+ gettimeofday(&tcur, (struct timezone *)0);
+ printf("\rTrack %02d: %4lld", track, bytes >> 20);
+ if (tracksize > 0)
+ printf(" of %4lld MB", tracksize >> 20);
+ else
+ printf(" MB");
+ printf(" written");
+ fper = fifo_percent(TRUE);
+ if (fper >= 0)
+ printf(" (fifo %3d%%)", fper);
+#ifdef BCAP
+ if (bsize > 0) { /* buffer size known */
+ usalp->silent++;
+ per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
+ usalp->silent--;
+ if (per >= 0) {
+ per = 100*(bsize - bfree) / bsize;
+ if (per < 5)
+ dp->cdr_dstat->ds_buflow++;
+ if (per < (int)dp->cdr_dstat->ds_minbuf &&
+ (startsec*secsize) > bsize) {
+ dp->cdr_dstat->ds_minbuf = per;
+ }
+ printf(" [buf %3d%%]", per);
+#ifdef BCAPDBG
+ printf(" %3ld %3ld", bsize >> 10, bfree >> 10);
+#endif
+ }
+ }
+#endif
+
+ tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
+ tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
+ while (tlast.tv_usec < 0) {
+ tlast.tv_usec += 1000000;
+ tlast.tv_sec -= 1;
+ }
+ fspeed = (nsecs / secsps) /
+ (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
+ if (fspeed > 999.0)
+ fspeed = 999.0;
+ printf(" %5.1fx", fspeed);
+ printf(".");
+ savbytes = (bytes >> 20) << 20;
+ flush();
+ neednl = TRUE;
+ tlast = tcur;
+ }
+ } while (tracksize < 0 || bytes_read < tracksize);
+
+ if ((bytes / secsize) < 300) {
+ if ((trackp->padsecs + (bytes / secsize)) < 300)
+ trackp->padsecs = 300 - (bytes / secsize);
+ }
+ if (trackp->padsecs > 0) {
+ Llong padbytes;
+
+ /*
+ * pad_track() is based on secsize. Compute the amount of bytes
+ * assumed by pad_track().
+ */
+ padbytes = (Llong)trackp->padsecs * secsize;
+
+ if (neednl) {
+ printf("\n");
+ neednl = FALSE;
+ }
+ if ((padbytes >> 20) > 0) {
+ neednl = TRUE;
+ } else if (lverbose) {
+ printf("Track %02d: writing %3lld KB of pad data.\n",
+ track, (Llong)(padbytes >> 10));
+ neednl = FALSE;
+ }
+ pad_track(usalp, dp, trackp, startsec, padbytes,
+ TRUE, &savbytes);
+ bytes += savbytes;
+ startsec += savbytes / secsize;
+ }
+ printf("%sTrack %02d: Total bytes read/written: %lld/%lld (%lld sectors).\n",
+ neednl?"\n":"", track, bytes_read, bytes, bytes/secsize);
+ return (0);
+}
diff --git a/wodim/wm_session.c b/wodim/wm_session.c
new file mode 100644
index 0000000..0505f9c
--- /dev/null
+++ b/wodim/wm_session.c
@@ -0,0 +1,51 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)wm_session.c 1.4 02/07/07 Copyright 1995, 1997 J. Schilling */
+/*
+ * CDR write method abtraction layer
+ * session at once / disk at once writing intercace routines
+ *
+ * Copyright (c) 1995, 1997 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <utypes.h>
+
+#include <usal/scsitransp.h>
+#include "wodim.h"
+
+extern int debug;
+extern int verbose;
+extern int lverbose;
+
+extern char *buf; /* The transfer buffer */
+
+int write_session_data(SCSI *usalp, cdr_t *dp, track_t *trackp);
diff --git a/wodim/wm_track.c b/wodim/wm_track.c
new file mode 100644
index 0000000..a3f9196
--- /dev/null
+++ b/wodim/wm_track.c
@@ -0,0 +1,50 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)wm_track.c 1.4 04/03/02 Copyright 1995, 1997, 2004 J. Schilling */
+/*
+ * CDR write method abtraction layer
+ * track at once writing intercace routines
+ *
+ * Copyright (c) 1995, 1997, 2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <standard.h>
+#include <utypes.h>
+
+#include "wodim.h"
+
+extern int debug;
+extern int verbose;
+extern int lverbose;
+
+extern char *buf; /* The transfer buffer */
+
+int write_track_data(cdr_t *dp, int track, track_t *trackp);
diff --git a/wodim/wodim.1 b/wodim/wodim.1
new file mode 100644
index 0000000..53e777c
--- /dev/null
+++ b/wodim/wodim.1
@@ -0,0 +1,2369 @@
+.\" @(#)wodim.1 06/12/18 Copyright 2006 Cdrkit maintainers
+.\" derived from:
+.\" @(#)cdrecord.1 1.106 06/02/09 Copyright 1996 J. Schilling
+.\"
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License version 2
+.\" as published by the Free Software Foundation.
+.\"
+.\" The GNU General Public License's references to "object code"
+.\" and "executables" are to be interpreted as the output of any
+.\" document formatting or typesetting system, including
+.\" intermediate and printed output.
+.\"
+.\" This manual 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; see the file COPYING. If not, write to the Free Software
+.\" Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.\"
+.if t .ds a \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'a
+.if t .ds o \v'-0.55m'\h'0.00n'\z.\h'0.45n'\z.\v'0.55m'\h'-0.45n'o
+.if t .ds u \v'-0.55m'\h'0.00n'\z.\h'0.40n'\z.\v'0.55m'\h'-0.40n'u
+.if t .ds A \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'A
+.if t .ds O \v'-0.77m'\h'0.25n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.70n'O
+.if t .ds U \v'-0.77m'\h'0.30n'\z.\h'0.45n'\z.\v'0.77m'\h'-0.75n'U
+.if t .ds s \\(*b
+.if t .ds S SS
+.if n .ds a ae
+.if n .ds o oe
+.if n .ds u ue
+.if n .ds s sz
+.if t .ds m \\(*m
+.if n .ds m micro
+.TH wodim 1 "Version 2.0" "" ""
+.SH NAME
+wodim \- write data to optical disk media
+.SH SYNOPSIS
+.B wodim
+.RI [ options "] " track1 .\|.\|. trackn
+.SH NOTE
+There may be similarities and differences between this program and other disk recording application(s). See the
+.B CREDITS
+and
+.B AUTHORS
+sections below to learn about the origin of
+.B wodim.
+
+.SH DESCRIPTION
+.B wodim
+is used to record data or audio Compact Discs on an Orange Book
+CD-Recorder or to write DVD media on a DVD-Recorder.
+.PP
+The
+.I device
+is the device file or label offered by the operating system to access the
+recorder with SCSI GENERIC (sg) interface. Note that some operating systems may
+provide separate device nodes for block\-oriented and sg access. For example, on
+older
+.I Linux
+systems, the sg access was available through
+.IR /dev/sg...
+files while the block oriented access was done through associated (but not identical)
+.IR /dev/hd...
+and
+.IR /dev/sr...
+(or
+.IR /dev/scd...
+) files.
+.PP
+In any case, the user running
+.B wodim
+needs read and write access to the particular device file on a Linux system. It
+is recommended to be root or install the application as suid-root, because
+certain versions of Linux (kernel) limit the set of SCSI commands allowed for
+non-root users. Even if usage without root identity is possible in many cases,
+some device drivers still may fail, show unexplainable problems and generally
+the problems become harder to debug. The risk for buffer-underruns is also
+increased. See the
+.IR "PROCESS SCHEDULING PRIORITY"
+section below for more details.
+.PP
+There is an alternative way of specifying the device, using the traditional SCSI descriptions in form of
+.IR devicetype:bus/target/lun
+specification. However, the success of this method is not guaranteed since it
+requires an adaptation scheme for your architecture, and the numbers may vary
+depending on the hardware-internal numbering or on the order of hot-plug device
+detection. If your operating system does not provide a sufficient framework for
+keeping this numbers persistent, don't rely on them. See
+.B \-scanbus
+and
+.B \-\-devices
+options below for details.
+.PP
+There are emulated SCSI compatible device systems, using the SCSI protocols
+transported over various hardware/media types. The most known examples is ATAPI
+("IDE burners") or USB storage ("external USB case"). If the pseudo-SCSI b/t/l
+device address specification is used instead of the native one, you need to
+prepend the "devicetype:" description to the emulated "bus/target/lun" device
+address.
+.PP
+If a file /etc/wodim.conf exists, the parameter to the
+.B dev=
+option may also be a drive name label in that file (see FILES section).
+.PP
+As a special exception, the device specification can be
+.IR -1
+or just omitted, which invokes automatic guessing of an appropriate device for
+the selected operation. However, this guessing is not available everywhere and
+is not reliable; it is only available for the user's convenience in simple
+environments.
+.PP
+In
+.I Track At Once
+mode, each
+.I track
+corresponds to a single file that contains the prepared data for that track.
+If the argument is
+.RB ` \- ',
+standard input is used for that track.
+Only one track may be taken from
+.IR stdin .
+In the other write modes, the direct file to track relation may not be implemented.
+In
+.B \-clone
+mode, a single file contains all data for the whole disk.
+To allow DVD writing on platforms that do not implement large file support,
+.B wodim
+concatenates all file arguments to a single track when writing to DVD media.
+
+.SH "PROCESS SCHEDULING PRIORITY"
+.PP
+Wodim tries to get higher process priority using different methods. This is
+important because the burn process is usually a realtime task, no long delays
+should occur while transmitting fresh data to the recorder. This is especially
+important on systems with insufficient RAM where swapping can create delays of
+many seconds.
+.PP
+A possible workaround on underpowered systems is the use of the burnfree or
+similar feature, allowing the recorder to resume.
+.PP
+Root permissions are usually required to get higher process scheduling priority.
+.PP
+On
+.B SVr4
+compliant systems,
+.B wodim
+uses the real time class to get the highest scheduling priority that is
+possible (higher than all kernel processes).
+On systems with
+.B POSIX real time scheduling
+wodim uses real time scheduling too,
+but may not be able to gain a priority that is higher than all kernel processes.
+.PP
+In order to be able to use the SCSI transport subsystem of the OS, run at highest
+priority and lock itself into core
+.B
+wodim
+either needs to be run as root, needs to be installed suid root or
+must be called via
+.B RBACs
+pfexec mechanism.
+
+.SH "GENERAL OPTIONS
+.PP
+General options must be before any track file name or track option.
+.TP
+.B \-version
+Print version information and exit.
+.TP
+.B \-v
+Increment the level of general verbosity by one.
+This is used e.g. to display the progress of the writing process.
+.TP
+.B \-V
+Increment the verbose level in respect of SCSI command transport by one.
+This helps to debug problems
+during the writing process, that occur in the CD/DVD-Recorder.
+If you get incomprehensible error messages you should use this flag
+to get more detailed output.
+.B \-VV
+will show data buffer content in addition.
+Using
+.B \-V
+or
+.B \-VV
+slows down the process and may be the reason for a buffer underrun.
+.TP
+.BI debug= "#, " -d
+Set the misc debug value to # (with debug=#) or increment
+the misc debug level by one (with -d). If you specify
+.I -dd,
+this equals to
+.BI debug= 2.
+This may help to find problems while opening a driver for libusal
+as well as with sector sizes and sector types.
+Using
+.B \-debug
+slows down the process and may be the reason for a buffer underrun.
+.TP
+.BR kdebug= "#, " kd= #
+Tell the
+.BR usal -driver
+to modify the kernel debug value while SCSI commands are running.
+.TP
+.BR \-silent ", " \-s
+Do not print out a status report for failed SCSI commands.
+.TP
+.B \-force
+Force to continue on some errors. Be careful when using this option.
+.B wodim
+implements several checks that prevent you from doing unwanted things
+like damaging CD-RW media by improper drives. Many of the sanity checks are
+disabled when the
+.B \-force
+option is used.
+.sp
+This option also implements some tricks that will allow
+you to blank bad CD-RW disks.
+.TP
+.B \-immed
+Tell wodim to set the
+.B "SCSI IMMED"
+flag in certain commands
+(load/eject/blank/close_track/close_session).
+This can be useful
+on broken systems with ATAPI harddisk and CD/DVD writer on the same bus or
+with SCSI systems that don't use disconnect/reconnect.
+These systems will freeze while blanking or fixating a CD/DVD or while a DVD
+writer is filling up a session to the minimum amount (approx. 800 MB).
+Setting the
+.B \-immed
+flag will request the command to return immediately
+while the operation proceeds in background, making
+the bus usable for the other devices and avoiding the system freeze.
+This is an experimental feature which may work or not, depending on the model
+of the CD/DVD writer.
+A correct solution would be to set up a correct cabling but there seem to be
+notebooks around that have been set up the wrong way by the manufacturer.
+As it is impossible to fix this problem in notebooks, the
+.B \-immed
+option has been added.
+.sp
+A second experimental feature of the
+.B \-immed
+flag is to tell wodim to try to wait short times while writing to the
+media. This is expected to free the IDE bus if the CD/DVD writer and the
+data source are connected to the same IDE cable. In this case, the CD/DVD
+writer would otherwise usually block the IDE bus for nearly all the time
+making it impossible to fetch data from the source drive. See also
+.B minbuf=
+and
+.B \-v
+option.
+.sp
+Use both features at your own risk.
+If it turns out that it would make sense to have a separate option
+for the wait feature, write to the author and convince him.
+.TP
+.BI minbuf= value
+The #
+.B minbuf=
+option allows to define the minimum drive buffer fill ratio for the
+experimental ATAPI wait mode that is intended to free the IDE bus
+to allow hard disk and CD/DVD writer to be on the same IDE cable.
+As the wait mode currently only works when the verbose option
+.B \-v
+has been specified,
+.B wodim
+implies the verbose option in case the
+.B \-immed
+or
+.B minbuf=
+option have been specified.
+Valid values for
+.B minbuf=
+are between 25 and 95 for 25%.\|.\|.95% minimum drive buffer fill ratio.
+.TP
+.B \-dummy
+The CD/DVD-Recorder will go through all steps of the recording process,
+but the laser is turned off during this procedure.
+It is recommended to run several tests before actually writing to a
+Compact Disk or Digital Versatile Disk,
+if the timing and load response of the system is not known.
+.TP
+.B \-clone
+Tells
+.B wodim
+to handle images created by
+.IR "readom \-clone" .
+The
+.B \-clone
+may only be used in conjunction with with the
+.B \-raw96r
+or with the
+.B \-raw16
+option.
+Using
+.B \-clone
+together with
+.B \-raw96r
+is preferred as it allows to write all subchannel data.
+The option
+.B \-raw16
+should only be used with drives that do not support to write in
+.B \-raw96r
+mode.
+.TP
+.B \-dao
+.TP
+.B \-sao
+Set
+.B "SAO (Session At Once)
+mode which is usually called
+.BR "Disk At Once " mode.
+This currently only works with MMC drives that support
+.B "Session At Once
+mode.
+Note that wodim needs to know the size of each track in advance for this mode
+(see the
+.B "genisoimage \-print-size"
+option and the
+.I EXAMPLES
+section for more information).
+.TP
+.B \-tao
+Set
+.B "TAO (Track At Once) writing mode.
+This is the default write mode in previous
+.B wodim
+versions.
+With most drives, this write mode is required for multi session recording.
+.TP
+.B \-raw
+Set
+.B "RAW writing mode.
+Using this option defaults to
+.BR \-raw96r .
+Note that wodim needs to know the size of each track in advance for this mode
+(see the
+.B "genisoimage \-print-size"
+option and the
+.I EXAMPLES
+section for more information).
+.TP
+.B \-raw96r
+Select
+Set
+.B "RAW writing mode
+with 2352 byte sectors plus 96 bytes of raw P-W subchannel data resulting
+in a sector size of 2448 bytes.
+This is the preferred raw writing mode as it gives best control over the
+CD writing process.
+If you find any problems with the layout of a disk or with sub channel
+content (e.g. wrong times on the display when playing the CD) and your drive
+supports to write in
+.B \-raw96r
+or
+.B \-raw16
+mode, you should give it a try. There are several CD writers with bad firmware
+that result in broken disks when writing in TAO or SAO mode.
+Writing data disks in raw mode needs significantly more CPU time than other
+write modes. If your CPU is too slow, this may result in buffer underruns.
+Note that wodim needs to know the size of each track in advance for this mode
+(see the
+.B "genisoimage \-print-size"
+option and the
+.I EXAMPLES
+section for more information).
+.TP
+.B \-raw96p
+Select
+Set
+.B "RAW writing mode
+with 2352 byte sectors plus 96 bytes of packed P-W subchannel data resulting
+in a sector size of 2448 bytes.
+This is the less preferred raw writing mode as only a few recorders support
+it and some of these recorders have bugs in the firmware implementation.
+Don't use this mode if your recorder supports
+.B \-raw96r
+or
+.BR \-raw16 .
+Writing data disks in raw mode needs significantly more CPU time than other
+write modes. If your CPU is too slow, this may result in buffer underruns.
+Note that wodim needs to know the size of each track in advance for this mode
+(see the
+.B "genisoimage \-print-size"
+option and the
+.I EXAMPLES
+section for more information).
+.TP
+.B \-raw16
+Select
+Set
+.B "RAW writing mode
+with 2352 byte sectors plus 16 bytes of P-Q subchannel data resulting
+in a sector size of 2368 bytes.
+If a recorder does not support
+.BR \-raw96r ,
+this is the preferred raw writing mode.
+It does not allow to write
+.I CD-Text
+or
+.I CD+Graphics
+but it is the only raw writing mode in cheap CD writers.
+As these cheap writers in most cases do not support
+.B \-dao
+mode.
+Don't use this mode if your recorder supports
+.BR \-raw96r .
+Writing data disks in raw mode needs significantly more CPU time than other
+write modes. If your CPU is too slow, this may result in buffer underruns.
+Note that wodim needs to know the size of each track in advance for this mode
+(see the
+.B "genisoimage \-print-size"
+option and the
+.I EXAMPLES
+section for more information).
+.TP
+.B \-multi
+Allow multi session CDs to be made. This flag needs to be present
+on all sessions of a multi session disk,
+except you want to create a session that will be
+the last session on the media.
+The fixation will be done in a way that allows the CD/DVD-Recorder to
+append additional sessions later. This is done by generation a TOC
+with a link to the next program area. The so generated media is not
+100% compatible to manufactured CDs (except for CDplus).
+Use only for recording of multi session CDs.
+If this option is present, the default track type is
+.BR "CD-ROM XA mode 2 form 1"
+and the sector size is 2048 bytes.
+The XA sector subheaders will be created by the drive.
+The
+.I Sony
+drives have no hardware support for
+.BR "CD-ROM XA mode 2 form 1" .
+You have to specify the
+.B \-data
+option in order to create multi session disks on these drives.
+As long as wodim does not have a coder for converting data sectors
+to audio sectors, you need to force
+.B CD-ROM
+sectors by including the
+.B \-data
+option if you like to record a multisession disk in SAO mode.
+Not all drives allow multisession CDs in SAO mode.
+.TP
+.B \-msinfo
+Retrieve multi session info in a form suitable for
+.B genisoimage
+and print it to standard output. See
+.B msifile=
+option for another version.
+.sp
+This option makes only sense with a CD that contains at least
+one closed session and is appendable (not finally closed yet).
+Some drives create error messages if you try to get the multi
+session info for a disk that is not suitable for this operation.
+.TP
+.BR msifile= filename
+Like
+.B \-msinfo
+option but also stores the multi session info in a file.
+.TP
+.B \-toc
+Retrieve and print out the table of content or PMA of a CD.
+With this option,
+.B wodim
+will work with CD-R drives and with CD-ROM drives.
+.TP
+.B \-atip
+Retrieve and print out the ATIP (absolute Time in Pre-groove) info of a CD/DVD
+recordable or CD/DVD re-writable media.
+With this option,
+.B wodim
+will try to retrieve the ATIP info. If the actual drive does not support
+to read the ATIP info, it may be that only a reduced set of information
+records or even nothing is displayed. Only a limited number of MMC compliant
+drives support to read the ATIP info.
+.sp
+If
+.B wodim
+is able to retrieve the lead-in start time for the first session, it will try to
+decode and print the manufacturer info from the media.
+DVD media does not have ATIP information but there is equivalent prerecorded
+information that is read out and printed.
+.TP
+.B \-fix
+The disk will only be fixated (i.e. a TOC for a CD-Reader will be written).
+This may be used, if for some reason the disk has been written but not
+fixated. This option currently does not work with old TEAC drives (CD-R50S and
+CD-R55S).
+.TP
+.B \-nofix
+Do not fixate the disk after writing the tracks. This may be used
+to create an audio disk in steps. An un-fixated disk can usually not be used
+on a non CD-writer type drive but there are audio CD players that will
+be able to play such a disk.
+.TP
+.B \-waiti
+Wait for input to become available on standard input before trying to open
+the SCSI driver. This allows
+.B wodim
+to read its input from a pipe even
+when writing additional sessions to a multi session disk.
+When writing another session to a multi session disk,
+.B genisoimage
+needs to read the old session from the device before writing output.
+This cannot be done if
+.B wodim
+opens the SCSI driver at the same time.
+.TP
+.B \-load
+Load the media and exit. This only works with a tray loading mechanism
+but seems to be useful when using the Kodak disk transporter.
+.TP
+.B \-lock
+Load the media, lock the door and exit. This only works with a tray loading mechanism
+but seems to be useful when using the Kodak disk transporter.
+.TP
+.B \-eject
+Eject disk after doing the work.
+Some devices (e.g. Philips) need to eject the medium before creating a new
+disk. Doing a \-dummy test and immediately creating a real disk would not
+work on these devices.
+.TP
+.BR speed= #
+Set the speed factor of the writing process to #.
+# is an integer, representing a multiple of the audio speed.
+This is about 150\ KB/s for CD-ROM, about 172\ KB/s for CD-Audio and about 1385\ kB/s
+for DVD media.
+If no
+.I speed
+option is present,
+.B wodim
+will try to get a drive specific speed value from the file
+.B /etc/wodim.conf
+and if it cannot find one, it will try to get the speed value from the
+.B CDR_SPEED
+environment and later from the
+.B CDR_SPEED=
+entry in
+.BR /etc/wodim.conf .
+If no speed value could be found, wodim uses a drive specific default speed.
+The default for all new (MMC compliant) drives is to use the maximum supported by the drive.
+If you use
+.I "speed=0"
+with a MMC compliant drive,
+.B wodim
+will switch to the lowest possible speed for drive and medium.
+If you are using an old (non MMC) drive that has problems with
+.I "speed=2
+or
+.IR "speed=4" ,
+you should try
+.IR "speed=0" .
+.TP
+.BI blank= type
+Blank a CD-RW and exit or blank a CD-RW before writing. The blanking type may be one of:
+.RS
+.TP 12
+help
+Display a list of possible blanking types.
+.TP
+all
+Blank the entire disk. This may take a long time.
+.TP
+fast
+Minimally blank the disk. This results in erasing the PMA, the TOC and the pregap.
+.TP
+track
+Blank a track.
+.TP
+unreserve
+Unreserve a reserved track.
+.TP
+trtail
+Blank the tail of a track.
+.TP
+unclose
+Unclose last session.
+.TP
+session
+Blank the last session.
+.RE
+Not all drives support all blanking types. It may be necessary to use
+.B "blank=all
+if a drive reports a specified command as being invalid.
+If used together with the
+.B \-force
+flag, this option may be used to blank CD-RW disks that otherwise cannot be
+blanked. Note that you may need to specify
+.BI blank= all
+because some drives will not continue with certain types of bad CD-RW
+disks. Note also that
+.B wodim
+does its best if the
+.B \-force
+flag is used but it finally depends on the drive's firmware
+whether the blanking operation will succeed or not.
+.TP
+.B \-format
+Format a CD-RW/DVD-RW/DVD+RW disc.
+Formatting is currently only implemented for DVD+RW media.
+A 'maiden' DVD+RW media needs to
+be formatted before you may write to it.
+However, as
+.B wodim
+autodetects the need for formatting in this case and auto formats the medium
+before it starts writing, the
+.B \-format
+option is only needed if you like to forcibly reformat a DVD+RW medium.
+.TP
+.BR fs= #
+Set the FIFO (ring buffer) size to #.
+You may use the same syntax as in
+.BR dd (1),
+.BR sdd (1)
+or
+.BR star (1).
+The number representing the size is taken in bytes unless otherwise specified.
+If a number is followed directly by the letter `b', `k', `m', `s' or `f',
+the size is multiplied by 512, 1024, 1024*1024, 2048 or 2352.
+If the size consists of numbers separated by `x' or `*', multiplication of the
+two numbers is performed.
+Thus
+.I "fs=10x63k
+will specify a FIFO size of 630\ kBytes.
+.sp
+The size specified by the
+.I fs=
+argument includes the shared memory that is needed for administration. This
+is at least one page of memory.
+If no
+.IR fs =
+option is present,
+.B wodim
+will try to get the FIFO size value from the
+.B CDR_FIFOSIZE
+environment.
+The default FIFO size is currently 4 MB.
+.sp
+The FIFO is used to increase buffering for the real time writing process.
+It allows to run a pipe from
+.B genisoimage
+directly into
+.BR wodim .
+If the FIFO is active and a pipe from
+.B genisoimage
+into
+.B wodim
+is used to create a CD,
+.B wodim
+will abort prior to do any modifications on the disk if
+.B genisoimage
+dies before it starts writing.
+The recommended FIFO size is between 4 and 128\ MBytes.
+As a rule of thumb, the FIFO size should be at least equal to the size
+of the internal buffer of the CD/DVD-Recorder and no more than half of
+the physical amount of RAM available in the machine.
+If the FIFO size is big enough, the FIFO statistics will print a FIFO
+empty count of zero and the FIFO min fill is not below 20%.
+It is not wise to use too much space for the FIFO. If you need more
+than 8 MB to write a CD at a speed less than 20x from an image on a
+local file system on an idle machine, your machine is either underpowered,
+has hardware problems or is mis-configured.
+If you like to write DVDs or CDs at higher speed, it makes sense
+to use at least 16\ MB for the FIFO.
+.sp
+On old and small machines, you need to be more careful with the FIFO size.
+If your machine has less than 256\ MB of physical RAM, you should not
+set up a FIFO size that is more than 32\ MB.
+The sun4c architecture (e.g. a Sparcstation-2) has only MMU page table entries
+for 16\ MBytes per process. Using more than 14\ MBytes for the FIFO
+may cause the operating system in this case to spend much time to constantly
+reload the MMU tables. Newer machines from Sun do not have this MMU
+hardware problem. I have no information on PC-hardware reflecting
+this problem.
+.sp
+Old Linux systems for non x86 platforms have broken definitions for
+the shared memory size. You need to fix them and rebuild the kernel
+or manually tell
+.B wodim
+to use a smaller FIFO.
+.sp
+If you have buffer underruns or similar problems (like a constantly empty
+drive buffer) and observe a zero
+.IR "fifo empty count" ,
+you have hardware problems that prevents the data from flowing fast enough
+from the kernel memory to the drive. The FIFO size in this case is sufficient,
+but you should check for a working DMA setup.
+.TP
+.BR ts= #
+Set the maximum transfer size for a single SCSI command to #.
+The syntax for the
+.B ts=
+option is the same as for wodim fs=# or sdd bs=#.
+.sp
+If no
+.B ts=
+option has been specified,
+.B wodim
+defaults to a transfer size of 63\ kB. If libusal gets lower values from the
+operating system, the value is reduced to the maximum value that is possible
+with the current operating system.
+Sometimes, it may help to further reduce the transfer size or to enhance it,
+but note that it may take a long time to find a better value by experimenting
+with the
+.B ts=
+option.
+.TP
+.BI dev= target
+Sets the SCSI target for the CD/DVD-Recorder, see notes above.
+A typical device specification is
+.BI dev= 6,0
+\&.
+A filename or virtual device name can be passed instead of the symbolic SCSI
+numbers. The correct device/filename in this case can be found in the system
+specific manuals of the target operating system.
+On a
+.I FreeBSD
+system without
+.I CAM
+support, you need to use the control device (e.g.
+.IR /dev/rcd0.ctl ).
+A correct device specification in this case may be
+.BI dev= /dev/rcd0.ctl:@
+\&.
+.sp
+On Linux and Windows 2000/XP, drives are accessible with their device (or
+drive) names or with the symbolic SCSI numbers (not recommended, mapping is not
+stable and could be completely removed in the future).
+.sp
+If no
+.I dev
+option is present,
+.B wodim
+will try to get the device from the
+.B CDR_DEVICE
+environment.
+.sp
+If the argument to the
+.B dev=
+option does not contain the characters ',', '/', '@' or ':',
+it is interpreted as an label name that may be found in the file
+/etc/wodim.conf (see FILES section).
+.TP
+.BI gracetime= #
+Set the grace time before starting to write to
+.IR # " seconds.
+Values below 2 seconds are not recommended to give the kernel or volume
+management a chance to learn the new state.
+.TP
+.BI timeout= #
+Set the default SCSI command timeout value to
+.IR # " seconds.
+The default SCSI command timeout is the minimum timeout used for sending
+SCSI commands.
+If a SCSI command fails due to a timeout, you may try to raise the
+default SCSI command timeout above the timeout value of the failed command.
+If the command runs correctly with a raised command timeout,
+please report the better timeout value and the corresponding command to
+the author of the program.
+If no
+.I timeout
+option is present, a default timeout of 40 seconds is used.
+.TP
+.BI driver= name
+Allows the user to manually select a driver for the device.
+The reason for the existence of the
+.BI driver= name
+option is to allow users to use
+.B wodim
+with drives that are similar to supported drives but not known
+directly by
+.BR wodim .
+All drives made after 1997 should be MMC standard compliant and
+thus supported by one of the MMC drivers.
+It is most unlikely that
+.B wodim
+is unable to find the right driver automatically.
+Use this option with extreme care. If a wrong driver is used for a
+device, the possibility of creating corrupted disks is high.
+The minimum problem related to a wrong driver is that the
+.B speed=
+or
+.B \-dummy
+will not work.
+.br
+.RS
+.ne 8
+.PP
+The following driver names are supported:
+.TP
+.B help
+To get a list of possible drivers together with a short description.
+.TP
+.B mmc_cd
+The generic SCSI-3/mmc CD-ROM driver is auto-selected whenever
+.B wodim
+finds a MMC compliant drive that does not identify itself to support writing at
+all, or that only identifies to support media or write modes not implemented in
+.BR wodim .
+.TP
+.B mmc_cd_dvd
+The generic SCSI-3/mmc CD/DVD driver is auto-selected whenever
+.B wodim
+finds a MMC-2 or MMC-3 compliant drive that seems to support more than
+one medium type and the tray is open or no medium could be found to select the
+right driver.
+This driver tries to close the tray, checks the medium found in the tray and then
+branches to the driver that matches the current medium.
+.TP
+.B mmc_cdr
+The generic SCSI-3/mmc CD-R/CD-RW driver is auto-selected whenever
+.B wodim
+find a MMC compliant drive that only supports to write CDs or a multi system
+drive that contains a CD as the current medium.
+.TP
+.B mmc_cdr_sony
+The generic SCSI-3/mmc CD-R/CD-RW driver is auto-selected whenever
+.B wodim
+would otherwise select the
+.B mmc_cdr
+driver but the device seems to be made by Sony.
+The
+.B mmc_cdr_sony
+is definitely needed for the Sony CDU 928 as this drive does not completely
+implement the MMC standard and some of the MMC SCSI commands have to be
+replaced by Sony proprietary commands. It seems that all Sony drives (even
+newer ones) still implement the Sony proprietary SCSI commands so it has
+not yet become a problem to use this driver for all Sony drives. If you find
+a newer Sony drive that does not work with this driver, please report.
+.TP
+.B mmc_dvd
+The generic SCSI-3/mmc-2 DVD-R/DVD-RW driver is auto-selected whenever
+.B wodim
+finds a MMC-2 or MMC-3 compliant drive that supports to write DVDs and
+an appropriate medium is loaded.
+There is no Track At Once mode for DVD writers.
+.TP
+.B mmc_dvdplus
+The generic SCSI-3/mmc-3 DVD+R/DVD+RW driver is auto-selected whenever
+one of the DVD+ media types that are incompatible to each other is found.
+It checks media and then
+branches to the driver that matches the current medium.
+.TP
+.B mmc_dvdplusr
+The generic SCSI-3/mmc-3 DVD+R driver is auto-selected whenever
+a DVD+R medium is found in an appropriate writer.
+Note that for unknown reason, the DVD-Plus alliance does not
+like that there is a simulation mode for DVD+R media.
+The author of
+.B wodim
+tries to convince manufacturers to implement a simulation mode for DVD+R
+and implement support.
+DVD+R only supports one write mode that is somewhere between Track At Once
+and Packet writing; this mode is selected in
+.B wodim
+via a the
+.BR \-dao / \-sao
+option.
+.TP
+.B mmc_dvdplusrw
+The generic SCSI-3/mmc-3 DVD+RW driver is auto-selected whenever
+a DVD+RW medium is found in an appropriate writer.
+As DVD+RW media needs to be formatted before its first use, wodim
+auto-detects this media state and performs a format before it starts
+to write.
+Note that for unknown reason, the DVD-Plus alliance does not
+like that there is a simulation mode nor a way to erase DVD+RW media.
+DVD+RW only supports one write mode that is close to
+Packet writing; this mode is selected in
+.B wodim
+via a the
+.BR \-dao / \-sao
+option.
+.TP
+.B cw_7501
+The driver for Matsushita/Panasonic CW-7501 is auto-selected when
+.B wodim
+finds this old pre MMC drive.
+.B wodim
+supports all write modes for this drive type.
+.TP
+.B kodak_pcd_600
+The driver for Kodak PCD-600 is auto-selected when
+.B wodim
+finds this old pre MMC drive which has been the first high speed (6x)
+CD writer for a long time. This drive behaves similar to the
+Philips CDD-521 drive.
+.TP
+.B philips_cdd521
+The driver for Philips CDD-521 is auto-selected when
+.B wodim
+finds a Philips CDD-521 drive (which is the first CD writer ever made)
+or one of the other drives that are known to behave similar to this
+drive.
+All Philips CDD-521 or similar drives (see other drivers in this list)
+do not support Session At Once recording.
+.TP
+.B philips_cdd521_old
+The driver for Philips old CDD-521 is auto-selected when
+.B wodim
+finds a Philips CDD-521 with very old firmware which has some known limitations.
+.TP
+.B philips_cdd522
+The driver for Philips CDD-522 is auto-selected when
+.B wodim
+finds a Philips CDD-522 which is the successor of the 521 or one of its variants
+with Kodak label.
+.B wodim
+does not support Session At Once recording with these drives.
+.TP
+.B philips_dumb
+The driver for Philips CDD-521 with pessimistic assumptions is never auto-selected.
+It may be used by hand with drives that behave similar to the Philips CDD-521.
+.TP
+.B pioneer_dws114x
+The driver for Pioneer DW-S114X is auto-selected when
+.B wodim
+finds one of the old non MMC CD writers from Pioneer.
+.TP
+.B plasmon_rf4100
+The driver for Plasmon RF 4100 is auto-selected when
+.B wodim
+finds this specific variant of the Philips CDD-521.
+.TP
+.B ricoh_ro1060c
+The driver for Ricoh RO-1060C is auto-selected when
+.B wodim
+finds this drive. There is no real support for this drive yet.
+.TP
+.B ricoh_ro1420c
+The driver for Ricoh RO-1420C is auto-selected when
+.B wodim
+finds a drive with this specific variant of the Philips CDD-521 command set.
+.TP
+.B scsi2_cd
+The generic SCSI-2 CD-ROM driver is auto-selected whenever
+.B wodim
+finds a pre MMC drive that does not support writing or a pre MMC writer that is
+not supported by
+.BR wodim .
+.TP
+.B sony_cdu924
+The driver for Sony CDU-924 / CDU-948 is auto-selected whenever
+.B wodim
+finds one of the old pre MMC CD writers from Sony.
+.TP
+.B teac_cdr50
+The driver for Teac CD-R50S, Teac CD-R55S, JVC XR-W2010, Pinnacle RCD-5020
+is auto-selected whenever one of the drives is found that is known to the
+non MMC command set used by TEAC and JVC.
+Note that many drives from JVC will not work because they do not correctly implement
+the documented command set and JVC has been unwilling to fix or document the
+bugs.
+There is no support for the Session At Once write mode yet.
+.TP
+.B tyuden_ew50
+The driver for Taiyo Yuden EW-50 is auto-selected when
+.B wodim
+finds a drive with this specific variant of the Philips CDD-521 command set.
+.TP
+.B yamaha_cdr100
+The driver for Yamaha CDR-100 / CDR-102 is auto-selected when
+.B wodim
+finds one of the old pre MMC CD writers from Yamaha.
+There is no support for the Session At Once write mode yet.
+.TP
+.B cdr_simul
+The simulation CD-R driver allows to run timing and speed tests
+with parameters that match the behavior of CD writers.
+.TP
+.B dvd_simul
+The simulation DVD-R driver allows to run timing and speed tests
+with parameters that match the behavior of DVD writers.
+.PP
+
+.sp
+There are two special driver entries in the list:
+.B cdr_simul
+and
+.BR dvd_simul .
+These driver entries are designed to make timing tests at any speed
+or timing tests for drives that do not support the
+.B \-dummy
+option.
+The simulation drivers implement a drive with a buffer size of 1\ MB
+that can be changed via the
+.B CDR_SIMUL_BUFSIZE
+environment variable.
+The simulation driver correctly simulates even a buffer underrun condition.
+If the
+.B \-dummy
+option is present, the simulation is not aborted in case of a buffer underrun.
+.RE
+.TP
+.BI driveropts= "option list"
+Set driver specific options. The options are specified a comma separated list.
+To get a list of valid options use
+.BI driveropts= help
+together with the
+.I \-checkdrive
+option.
+If you like to set driver options without running a typical
+.B wodim
+task, you need to use the
+.B \-setdropts
+option in addition, otherwise the command line parser in
+.B wodim
+will complain.
+Currently implemented driver options are:
+.RS
+.TP
+.B burnfree
+Turn the support for Buffer Underrun Free writing on.
+This only works for drives that support Buffer Underrun Free technology, which
+is available on most drives manufactured in this millennium.
+This may be called:
+.BR "Sanyo BURN-Proof" ,
+.BR "Ricoh Just-Link" ,
+.B "Yamaha Lossless-Link"
+or similar.
+.sp
+This option is deprecated and is mentioned here for documentation purposes
+only. The BURN-Free feature is enabled by default if the drive supports it.
+However, use of BURN-Free may cause decreased burning quality. Therefore it can
+be useful to disable it for certain purposes, eg. when creating a master copy
+for mass CD production.
+.TP
+.B noburnfree
+Turn the support for Buffer Underrun Free writing off.
+.TP
+.BI varirec= value
+Turn on the
+.B "Plextor VariRec"
+writing mode. The mandatory parameter
+.I value
+is the laser power offset and currently may be selected from
+-2, -1, 0, 1, 2.
+In addition, you need to set the write speed to 4 in order to allow
+.B "VariRec"
+to work.
+.TP
+.BI gigarec= value
+Manage the
+.B "Plextor GigaRec"
+writing mode. The mandatory parameter
+.I value
+is the disk capacity ratio compared to normal recording and currently may be selected from
+0.6, 0.7, 0.8, 1.0, 1.2, 1.3, 1.4.
+If values < 1.0 are used, then the effect is similar to the
+.B "Yamaha Audio Master Q. R."
+feature. If values > 1.0 are used, then the disk capacity is
+increased.
+.sp
+Not all drives support all
+.B GigaRec
+values.
+When a drive uses the
+.B GigaRec
+feature, the write speed is limited to 8x.
+.TP
+.B audiomaster
+Turn on the
+.B "Yamaha Audio Master Q. R."
+feature which usually should result in high quality CDs that
+have less reading problems in Hi-Fi players.
+As this is implemented as a variant of the
+Session at Once write mode, it will only work if you select
+SAO write mode and there is no need to turn it off.
+The
+.B "Audio Master"
+mode will work with a limited speed but
+may also be used with data CDs. In
+.B "Audio Master"
+mode, the pits on the CD will be written larger then usual so the capacity
+of the medium is reduced when turning this feature on.
+A 74 minute CD will only have a capacity of 63 minutes if
+.B "Audio Master"
+is active and the capacity of a 80 minute CD will be reduced to 68 minutes.
+.TP
+.B forcespeed
+Normally, modern drives know the highest possible speed for different
+media and may reduce the speed in order to grant best write quality.
+This technology may be called:
+.BR "Plextor PowerRec" ,
+.BR "Ricoh Just-Speed" ,
+.B "Yamaha Optimum Write Speed Control"
+or similar.
+Some drives (e.g. Plextor, Ricoh and Yamaha) allow to force the drive to
+use the selected speed even if the medium is so bad that the
+write quality would be poor. This option tells such a drive to
+force to use the selected speed regardless of the medium quality.
+.sp
+Use this option with extreme care and note that the drive should know better
+which medium will work at full speed.
+The default is to turn
+.B forcespeed
+off, regardless of the defaults of the drive.
+.TP
+.B noforcespeed
+Turn off the
+.B "force speed
+feature.
+.TP
+.B speedread
+Some ultra high speed drives such as 48x and faster drives from Plextor
+limit the read speed for unknown media to e.g. 40x in order to avoid
+damaged disks and drives.
+Using this option tells the drive to read any media as fast as possible.
+Be very careful as this may cause the media to break in the drive
+while reading, resulting in a damaged media and drive!
+.TP
+.B nospeedread
+Turn off unlimited read speed.
+.TP
+.B singlesession
+Turn the drive into a single session only drive.
+This allows to read defective or non-compliant (illegal) media with extremely
+non-standard additional (broken/illegal) TOC entries in the TOC from the second
+or higher session. Some of these disks become
+usable if only the information from the first session is used.
+You need to enable Single Session mode before you insert the defective disk!
+.TP
+.B nosinglesession
+Turn off single session mode. The drive will again behave as usual.
+.TP
+.B hidecdr
+Hide the fact that a medium might be a recordable medium.
+This allows to make CD-Rs look like CD-ROMs and applications believe
+that the media in the drive is not a CD-R.
+.TP
+.B nohidecdr
+Turn off hiding CD-R media.
+.TP
+.B tattooinfo
+Use this option together with
+.B \-checkdrive
+to retrieve the image size information for the
+.B "Yamaha DiskT@2
+feature. The images always have a line length of 3744 pixel.
+Line number 0 (radius 0) is mapped to the center of the disk.
+If you know the inner and outer radius you will be able to create a
+pre distorted image that later may appear undistorted on the disk.
+.TP
+.BI tattoofile= name
+Use this option together with
+.B \-checkdrive
+to write an image prepared for the
+.B "Yamaha DiskT@2
+feature to the medium.
+The file must be a file with raw image B&W data (one byte per pixel)
+in a size as retrieved by a previous call to
+.BI tattoofile= name
+\&.
+If the size of the image equals the maximum possible size
+(3744 x 320 pixel),
+.B wodim
+will use the first part of the file. This first part then will
+be written to the leftover space on the CD.
+.sp
+Note that the image must be mirrored to be readable from the pick up
+side of the CD.
+.RE
+.TP
+.B \-setdropts
+Set the driveropts specified by
+.BI driveropts= "option list" ,
+the
+.B speed
+of the drive and the
+.B dummy
+flag and exit.
+This allows wodim to set drive specific parameters that are not directly
+used by
+.B wodim
+like e.g.
+.BR "single session mode" ", " "hide cdr"
+and similar.
+It is needed in case that
+.BI driveropts= "option list"
+should be called without planning to run a typical
+.B wodim
+task.
+.TP
+.B \-checkdrive
+Checks if a driver for the current drive is present and exit.
+If the drive is a known drive,
+.B wodim
+uses exit code 0.
+.TP
+.B \-prcap
+Print the drive capabilities for SCSI-3/mmc compliant drives
+as obtained from mode page 0x2A. Values marked with
+.I kB
+use 1000 bytes as kilo-byte, values marked with
+.I KB
+use 1024 bytes as Kilo-byte.
+.TP
+.B \-inq
+Do an inquiry for the drive, print the inquiry info and exit.
+.TP
+.B \-scanbus
+Scan all SCSI devices on all SCSI busses and print the inquiry
+strings. This option may be used to find SCSI address of the
+CD/DVD-Recorder on a system. If some device types are invisible, try using
+.B dev=ATA:
+or similar option to give a hint about the device type you are looking for.
+The numbers printed out as labels are computed by:
+.B "bus * 100 + target.
+On platforms and device systems without persistent SCSI number management the
+results are not reliable. Use the .B \-\-devices option instead.
+.TP
+.B \-\-devices
+Look for useable devices using the system specific functions, eg. probing with
+usual device nodes in /dev/*, and display the detections using symbolic device
+names in OS specific syntax.
+.TP
+.B \-reset
+Try to reset the SCSI bus where the CD recorder is located. This works not
+on all operating systems.
+.TP
+.B \-abort
+Try to send an
+.B abort
+sequence to the drive.
+If you use
+.B wodim
+only, this should never be needed; but other software may leave a drive
+in an unusable condition.
+Calling
+.B "wodim \-reset
+may be needed if a previous write has been interrupted and the software did
+not tell the drive that it will not continue to write.
+.TP
+.B \-overburn
+Allow
+.B wodim
+to write more than the official size of a medium. This feature is usually
+called
+.I overburning
+and depends on the fact that most blank media may hold more space than the
+official size. As the official size of the lead-out area on the disk is
+90 seconds (6750 sectors) and a disk usually works if there are at least
+150 sectors of lead out, all media may be overburned by at least 88 seconds
+(6600 sectors).
+Most CD recorders only do overburning in
+.B SAO
+or
+.B RAW
+mode. Known exceptions are TEAC CD-R50S, TEAC CD-R55S and the Panasonic
+CW-7502.
+Some drives do not allow to overburn as much as you might like and limit
+the size of a CD to e.g. 76 minutes. This problem may be circumvented by
+writing the CD in RAW mode because this way the drive has no chance to find
+the size before starting to burn.
+There is no guarantee that your drive supports overburning at all.
+Make a test to check if your drive implements the feature.
+.TP
+.B \-ignsize
+Ignore the known size of the medium. This option should be used with extreme
+care, it exists only for debugging purposes don't use it for other reasons.
+It is not needed to write disks with more than the nominal capacity.
+This option implies
+.BR \-overburn .
+.TP
+.B \-useinfo
+Use
+.B "*.inf
+files to overwrite audio options.
+If this option is used, the pregap size information is read from
+the
+.B "*.inf
+file that is associated with the file that contains the audio
+data for a track.
+.sp
+If used together with the
+.B \-audio
+option,
+.B wodim
+may be used to write audio CDs from a pipe from
+.B icedax
+if you call
+.B wodim
+with the
+.B *.inf
+files as track parameter list instead of using audio files.
+The audio data is read from
+.B stdin
+in this case.
+See
+.B EXAMPLES
+section below.
+.B wodim
+first verifies that
+.B stdin
+is not connected to a terminal and runs some heuristic consistency checks
+on the
+.B *.inf
+files and then sets the track lengths from the information in
+the
+.B *.inf
+files.
+.sp
+If you like to write from
+.BR stdin ,
+make sure that wodim is called with a large enough FIFO size, reduce the write
+speed to a value below the read speed of the source drive and switch the burn-free
+option for the recording drive on.
+.TP
+.BR defpregap= #
+Set the default pre-gap size for all tracks except track number 1.
+This option currently only makes sense with the TEAC drive when
+creating track-at-once disks without the 2 second silence before each track.
+.br
+This option may go away in future.
+.TP
+.B \-packet
+Set
+.B "Packet writing mode.
+This is an experimental interface.
+.TP
+.BR pktsize= #
+Set the packet size to #, forces fixed packet mode.
+This is an experimental interface.
+.TP
+.B \-noclose
+Do not close the current track, useful only when in packet writing mode.
+This is an experimental interface.
+.TP
+.BI mcn= med_cat_nr
+Set the
+.B "Media Catalog Number
+of the CD to
+.IR med_cat_nr .
+.TP
+.B \-text
+Write CD-Text information
+based on information taken from a file that contains ascii information
+for the text strings.
+.B wodim
+supports CD-Text information based on the content of the
+.B *.inf
+files created by
+.B icedax
+and CD-Text information based on the content from a
+.B "CUE sheet
+file.
+If a
+.B "CUE sheet
+file contains both (binary CDTEXTFILE and text based SONGWRITER)
+entries, then the information based on the CDTEXTFILE entry will win.
+.sp
+You need to use the
+.B \-useinfo
+option in addition in order to tell
+.B wodim
+to read the
+.B "*.inf
+files or
+.BI cuefile= filename
+in order to tell
+.B wodim
+to read a
+.B CUE sheet
+file in addition.
+If you like to write your own CD-Text information,
+edit the
+.B *.inf
+files or the
+.B "CUE sheet
+file with a text editor and change the fields
+that are relevant for CD-Text.
+.TP
+.BI textfile= filename
+Write CD-Text based on information found in the binary file
+.IR filename .
+This file must contain information in a data format defined in the
+SCSI-3 MMC-2 standard and in the Red Book. The four byte size header that is
+defined in the SCSI standard is optional and allows to make the recognition of
+correct data less ambiguous.
+This is the best option to be used to copy CD-Text data from existing CDs
+that already carry CD-Text information. To get data in a format suitable
+for this option use
+.B wodim \-vv \-toc
+to extract the information from disk.
+If both,
+.BI textfile= filename
+and CD-Text information from
+.B *.inf
+or
+.B *.cue
+files are present,
+.BI textfile= filename
+will overwrite the other information.
+.TP
+.BI cuefile= filename
+Take all recording related information from a CDRWIN compliant
+.B "CUE sheet
+file.
+No track files are allowed when this option is present and the option
+.B \-dao
+is currently needed in addition.
+
+.SH "TRACK OPTIONS
+.PP
+Track options may be mixed with track file names.
+.TP
+.BI isrc= ISRC_number
+Set the
+.B "International Standard Recording Number
+for the next track to
+.IR ISRC_number .
+.TP
+.BI index= list
+Sets an index list for the next track.
+In index list is a comma separated list of numbers that are counting
+from index 1. The first entry in this list must contain a 0, the following
+numbers must be an ascending list of numbers (counting in 1/75 seconds) that
+represent the start of the indices. An index list in the form:
+0,7500,15000 sets index 1 to the start of the track, index 2 100 seconds from
+the start of the track and index 3 200 seconds from the start of the track.
+.TP
+.B \-audio
+If this flag is present, all subsequent tracks are written in
+.B "CD-DA
+(similar to Red Book) audio format.
+The file with data for this tracks should
+contain stereo, 16-bit digital audio with 44100 samples/s.
+The byte order should be the following: MSB left, LSB left,
+MSB right, LSB right, MSB left and so on. The track should be a multiple of
+2352 bytes. It is not possible to put the master image of an audio track
+on a raw disk because
+data will be read in multiple of 2352 bytes during the recording process.
+.sp
+If a filename ends in
+.I .au
+or
+.I .wav
+the file is considered to be a structured audio data file.
+.B wodim
+assumes that the file in this case is a Sun audio file or a
+Microsoft .WAV file
+and extracts the audio data from the files by skipping over the
+non-audio header information.
+In all other cases, wodim will only work correctly if the
+audio data stream does not have any header.
+Because many structured audio files do not have an integral
+number of blocks (1/75th second) in length,
+it is often necessary to specify the
+.B \-pad
+option as well.
+.B wodim
+recognizes that audio data in a .WAV file is stored in Intel
+(little-endian) byte order, and will automatically byte-swap the data
+if the CD recorder requires big-endian data.
+.B wodim
+will reject any audio file that does not match the Red Book requirements
+of 16-bit stereo samples in PCM coding at 44100 samples/second.
+.sp
+Using other structured audio data formats as input to
+.B wodim
+will usually work if the structure of the data is the
+structure described above (raw pcm data in big-endian byte order).
+However, if the data format includes a header,
+you will hear a click at the start of a track.
+.TP
+.I " "
+If neither
+.I \-data
+nor
+.I \-audio
+have been specified,
+.B wodim
+defaults to
+.I \-audio
+for all filenames that end in
+.I .au
+or
+.I .wav
+and to
+.I \-data
+for all other files.
+.TP
+.B \-swab
+If this flag is present, audio data is assumed to be in byte-swapped
+(little-endian) order. Some types of CD-Writers e.g. Yamaha, Sony and the
+new SCSI-3/mmc drives require audio data to be presented in
+little-endian order,
+.\" (which is the order in which it's actually recorded on the CD) ????
+while other writers require audio data to be
+presented in the big-endian (network) byte order normally used by the
+SCSI protocol.
+.B wodim
+knows if a CD-Recorder needs audio data in big- or little-endian order,
+and corrects the byte order of the data stream to match the needs
+of the recorder.
+You only need the
+.I \-swab
+flag if your data stream is in Intel (little-endian) byte order.
+.sp
+Note that the verbose output of
+.B wodim
+will show you if swapping is necessary to make the byte order of
+the input data fit the required byte order of the recorder.
+.B wodim
+will not show you if the
+.I \-swab
+flag was actually present for a track.
+.TP
+.B \-data
+If this flag is present, all subsequent tracks are written in
+.B "CD-ROM mode 1
+(Yellow Book) format. The data size is a multiple of 2048 bytes.
+The file with track data should contain an
+.BR ISO-9660 " or " "Rock Ridge
+filesystem image (see
+.B genisoimage
+for more details). If the track data is an
+.B ufs
+filesystem image, fragment size should be set to 2\ KB or more to allow
+CD-drives with 2\ KB sector size to be used for reading.
+.TP
+.I " "
+.I \-data
+is the default, if no other flag is present and the file does not
+appear to be of one of the well known audio file types.
+.TP
+.I " "
+If neither
+.I \-data
+nor
+.I \-audio
+have been specified,
+.B wodim
+defaults to
+.I \-audio
+for all filenames that end in
+.I .au
+or
+.I .wav
+and to
+.I \-data
+for all other files.
+.TP
+.B \-mode2
+If this flag is present, all subsequent tracks are written in
+.B "CD-ROM mode 2
+format. The data size is a multiple of 2336 bytes.
+.TP
+.B \-xa
+If this flag is present, all subsequent tracks are written in
+.B "CD-ROM XA mode 2 form 1
+format. The data size is a multiple of 2048 bytes.
+The XA sector sub headers will be created by the drive.
+With this option, the write mode is the same as with the
+.B \-multi
+option.
+.TP
+.B \-xa1
+If this flag is present, all subsequent tracks are written in
+.B "CD-ROM XA mode 2 form 1
+format. The data size is a multiple of 2056 bytes.
+The XA sector sub headers are part of the user data and have to be
+supplied by the application that prepares the data to be written.
+.TP
+.B \-xa2
+If this flag is present, all subsequent tracks are written in
+.B "CD-ROM XA mode 2 form 2
+format. The data is a multiple of 2324 bytes.
+The XA sector sub headers will be created by the drive.
+.TP
+.B \-xamix
+If this flag is present, all subsequent tracks are written in a way
+that allows a mix of
+.B "CD-ROM XA mode 2 form 1/2
+format. The data size is a multiple of 2332 bytes.
+The XA sector sub headers are part of the user data and have to be
+supplied by the application that prepares the data to be written.
+The CRC and the P/Q parity ECC/EDC information (depending on the sector
+type) have to be supplied by the application that prepares the data to be written.
+.TP
+.B \-cdi
+If this flag is present, the TOC type for the disk is set to
+.BR CDI .
+This only makes sense with XA disks.
+.TP
+.B \-isosize
+Use the
+.B "ISO-9660
+file system size as the size of the next track.
+This option is needed if you want
+.B wodim
+to directly read the image of a track from
+a raw disk partition or from a
+.I TAO
+master CD. In the first case the option
+.B \-isosize
+is needed to limit the size of the CD to the size of the ISO filesystem.
+In the second case the option
+.B \-isosize
+is needed to prevent
+.B wodim
+from reading the two run out blocks that are appended by each CD-recorder
+in track at once mode. These two run out blocks cannot be read and would
+cause a buffer underrun that would cause a defective copy.
+Do not use this option on files created by
+.B genisoimage
+and in case
+.B wodim
+reads the track data from
+.IR stdin .
+In the first case, you would prevent
+.B wodim
+from writing the amount of padding that has been appended by
+.B genisoimage
+and in the latter case, it will not work because
+.I stdin
+is not seekable.
+.sp
+If
+.B \-isosize
+is used for a track,
+.B wodim
+will automatically add padding for this track as if the
+.B \-pad
+option has been used but the amount of padding may be less than the padding
+written by
+.BR genisoimage .
+Note that if you use
+.B \-isosize
+on a track that contains Sparc boot information, the boot information will
+be lost.
+.sp
+Note also that
+this option cannot be used to determine the size of a file system
+if the multi session option is present.
+.TP
+.B \-pad
+If the track is a data track, 15 sectors of zeroed data
+will be added to the end of this and each subsequent data track.
+In this case, the
+.B \-pad
+option is superseded by the
+.B padsize=
+option. It will remain however as a shorthand for
+.BI padsize= 15s.
+If the
+.I \-pad
+option refers to an audio track,
+.B wodim
+will pad the audio data to be a multiple of 2352 bytes.
+The audio data padding is done with binary zeroes which is
+equal to absolute silence.
+.sp
+.B \-pad
+remains valid until disabled by
+.BR \-nopad .
+.TP
+.BR padsize= #
+Set the amount of data to be appended as padding to the next track to #.
+Opposed to the behavior of the
+.B \-pad
+option, the value for
+.I padsize=
+is reset to zero for each new track.
+wodim assumes a sector size of 2048 bytes for the
+.I padsize=
+option, independent from the real
+sector size and independent from the write mode.
+The megabytes mentioned in the verbose mode output however are counting
+the output sector size which is e.g. 2448 bytes when writing in RAW/RAW96
+mode.
+See
+.BR fs =
+option for possible arguments.
+To pad the equivalent of 20 minutes on a CD, you may write
+.BR padsize= 20x60x75s.
+Use this option if your CD-drive is not able to read the last sectors of
+a track or if you want to be able to read the CD
+on a
+.B Linux
+system with the ISO-9660 filesystem read ahead bug.
+If an empty file is used for track data,
+this option may be used to create a disk that is entirely made of padding.
+This may e.g. be used to find out how much overburning is possible with a
+specific media.
+.TP
+.B \-nopad
+Do not pad the following tracks \- the default.
+.TP
+.B \-shorttrack
+Allow all subsequent tracks to violate the Red Book track length standard
+which requires a minimum track length of 4 seconds.
+This option is only useful when used in SAO or RAW mode.
+Not all drives support this feature. The drive must accept the
+resulting CUE sheet or support RAW writing.
+.TP
+.B \-noshorttrack
+Re-enforce the Red Book track length standard. Tracks must be
+at least 4 seconds.
+.TP
+.BR pregap= #
+Set the pre-gap size for the next track.
+This option currently only makes sense with the TEAC drive when
+creating track-at-once disks without the 2 second silence before each track.
+.br
+This option may go away in future.
+.TP
+.B \-preemp
+If this flag is present, all TOC entries for subsequent audio tracks
+will indicate that the audio data has been sampled with 50/15 \*msec
+pre-emphasis.
+The data, however is not modified during the process of transferring from file
+to disk.
+This option has no effect on data tracks.
+.TP
+.B \-nopreemp
+If this flag is present, all TOC entries for subsequent audio tracks
+will indicate that the audio data has been mastered with linear data \-
+this is the default.
+.TP
+.B \-copy
+If this flag is present, all TOC entries for subsequent audio tracks
+of the resulting CD
+will indicate that the audio data has permission to be copied without limit.
+This option has no effect on data tracks.
+.TP
+.B \-nocopy
+If this flag is present, all TOC entries for subsequent audio tracks
+of the resulting CD
+will indicate that the audio data has permission to be copied only once for
+personal use \-
+this is the default.
+.TP
+.B \-scms
+If this flag is present, all TOC entries for subsequent audio tracks
+of the resulting CD
+will indicate that the audio data has no permission to be copied anymore.
+.TP
+.BR tsize= #
+If the master image for the next track has been stored on a raw disk,
+use this option
+to specify the valid amount of data on this disk. If the image of the next
+track is stored in a regular file, the size of that file is taken to determine
+the length of this track.
+If the track contains an ISO 9660 filesystem image use the
+.I \-isosize
+option to determine the length of that filesystem image.
+.br
+In Disk at Once mode and with some drives that use
+the TEAC programming interface, even in Track at Once mode,
+.B wodim
+needs to know the size of each track before starting to write the disk.
+wodim now checks this and aborts before starting to write.
+If this happens you will need to run
+.B "genisoimage -print-size
+before and use the output (with `s' appended) as an argument to the
+.BR tsize =
+option of
+.B wodim
+(e.g. tsize=250000s).
+.br
+See
+.BR fs =
+option for possible arguments.
+
+.SH EXAMPLES
+.PP
+For all examples below, it will be assumed that the CD/DVD-Recorder is
+connected to the primary SCSI bus of the machine. The SCSI target id is
+set to 2.
+.PP
+To record a pure CD-ROM at double speed, using data from the file
+.IR cdimage.raw :
+.PP
+ wodim \-v speed=2 dev=2,0 cdimage.raw
+.PP
+To create an image for a ISO 9660 filesystem with Rock Ridge extensions:
+.PP
+ genisoimage \-R \-o cdimage.raw /home/joerg/master/tree
+.PP
+To check the resulting file before writing to CD on Solaris:
+.PP
+ mount \-r \-F fbk \-o type=hsfs /dev/fbk0:cdimage.raw /mnt
+.PP
+On Linux:
+.PP
+ mount cdimage.raw \-r \-t iso9660 \-o loop /mnt
+.PP
+Go on with:
+.br
+ ls \-lR /mnt
+.br
+ umount /mnt
+.PP
+If the overall speed of the system is sufficient and the structure of
+the filesystem is not too complex, wodim will run without creating an
+image of the ISO 9660 filesystem. Simply run the pipeline:
+.PP
+ genisoimage \-R /master/tree | wodim \-v fs=6m speed=2 dev=2,0 -
+.PP
+The recommended minimum FIFO size for running this pipeline is 4 MBytes.
+As the default FIFO size is 4 MB, the
+.B fs=
+option needs only be present if you want to use a different FIFO size.
+If your system is loaded, you should run genisoimage in the real time class too.
+To raise the priority of
+.B genisoimage
+replace the command
+.PP
+ genisoimage \-R /master/tree
+.br
+by
+.br
+ priocntl \-e \-c RT \-p 59 genisoimage \-R /master/tree
+.sp
+on Solaris and by
+.sp
+ nice --18 genisoimage \-R /master/tree
+.sp
+on systems that don't have
+.B "UNIX International
+compliant real-time scheduling.
+.PP
+wodim runs at priority 59 on Solaris, you should run genisoimage
+at no more than priority 58. On other systems, you should run genisoimage
+at no less than nice --18.
+.PP
+Creating a CD-ROM without file system image on disk has been tested
+on a Sparcstation-2 with a Yamaha CDR-400. It did work up to quad speed
+when the machine was not loaded.
+A faster machine may be able to handle quad speed also in the loaded case.
+.PP
+To record a pure CD-DA (audio) at single speed, with each track contained
+in a file named
+.IR track01.cdaudio ,
+.IR track02.cdaudio ,
+etc:
+.PP
+ wodim \-v speed=1 dev=/dev/cdrw -audio track*.cdaudio
+.PP
+To check if it will be ok to use double speed for the example above.
+Use the dummy write option:
+.PP
+ wodim \-v \-dummy speed=2 dev=/dev/cdrw \-audio track*.cdaudio
+.PP
+To record a mixed-mode CD with an ISO 9660 filesystem from
+.I cdimage.raw
+on the first track, the other tracks being audio tracks from the files
+.IR track01.cdaudio ,
+.IR track02.cdaudio ,
+etc:
+.PP
+ wodim \-v dev=2,0 cdimage.raw \-audio track*.cdaudio
+.PP
+To handle drives that need to know the size of a track before starting to write,
+first run
+.PP
+ genisoimage -R -q -print-size /master/tree
+.PP
+and then run
+.PP
+ genisoimage -R /master/tree | wodim speed=2 dev=2,0 tsize=XXXs -
+.PP
+where
+.I XXX
+is replaced by the output of the previous run of genisoimage.
+.PP
+To copy an audio CD in the most accurate way, first run
+.PP
+ icedax dev=/dev/cdrom \-vall cddb=0 -B \-Owav
+.PP
+and then run
+.PP
+ wodim dev=/dev/cdrw \-v \-dao \-useinfo \-text *.wav
+.PP
+This will try to copy track indices and to read CD-Text information from disk.
+If there is no CD-Text information,
+.B icedax
+will try to get the information from freedb.org instead.
+.PP
+To copy an audio CD from a pipe (without intermediate files), first run
+.PP
+ icedax dev=1,0 \-vall cddb=0 \-info-only
+.PP
+and then run
+.PP
+ icedax dev=1,0 \-no-infofile \-B \-Oraw \- | \\
+.br
+ wodim dev=2,0 \-v \-dao \-audio \-useinfo \-text *.inf
+.PP
+This will get all information (including track size info) from the
+.B *.inf
+files and then read the audio data from stdin.
+.sp
+If you like to write from
+.BR stdin ,
+make sure that wodim is called with a large enough FIFO size (e.g.
+.BR fs=128m ),
+reduce the write speed to a value below the read speed of the source drive
+(e.g.
+.BR speed=12 ),
+and get a CD/DVD drive with BURN-Free feature if it is not available yet.
+.PP
+To set drive options without writing a CD (e.g. to switch a drive
+to single session mode), run
+.PP
+ wodim dev=1,0 \-setdropts driveropts=singlesession
+.PP
+If you like to do this when no CD is in the drive, call
+.PP
+ wodim dev=1,0 \-force \-setdropts driveropts=singlesession
+.PP
+To copy a CD in clone mode, first read the master CD using:
+.PP
+ readom dev=b,t,l \-clone f=somefile
+.PP
+or (in case the CD contains many sectors that are unreadable by intention)
+by calling:
+.PP
+ readom dev=1,0 -clone -nocorr f=somefile
+.PP
+will create the files
+.I somefile
+and
+.IR somefile.toc .
+Then write the CD using:
+.PP
+ wodim dev=1,0 -raw96r -clone -v somefile
+
+
+.SH ENVIRONMENT
+.TP
+.B CDR_DEVICE
+This may either hold a device identifier that is suitable to the open
+call of the SCSI transport library or a label in the file /etc/wodim.conf.
+.TP
+.B CDR_SPEED
+Sets the default speed value for writing (see also
+.B speed=
+option).
+.TP
+.B CDR_FIFOSIZE
+Sets the default size of the FIFO (see also
+.BR fs= #
+option).
+.TP
+.B CDR_FORCERAWSPEED
+If this environment variable is set,
+.B wodim
+will allow you to write at the full RAW encoding speed a single CPU supports.
+This will create high potential of buffer underruns. Use with care.
+.TP
+.B CDR_FORCESPEED
+If this environment variable is set,
+.B wodim
+will allow you to write at the full DMA speed the system supports.
+There is no DMA reserve for reading the data that is to be written from disk.
+This will create high potential of buffer underruns. Use with care.
+.TP
+.B RSH
+If the
+.B RSH
+environment is present, the remote connection will not be created via
+.BR rcmd (3)
+but by calling the program pointed to by
+.BR RSH .
+Use e.g.
+.BR RSH= /usr/bin/ssh
+to create a secure shell connection.
+.sp
+Note that this forces
+.B wodim
+to create a pipe to the
+.B rsh(1)
+program and disallows
+.B wodim
+to directly access the network socket to the remote server.
+This makes it impossible to set up performance parameters and slows down
+the connection compared to a
+.B root
+initiated
+.B rcmd(3)
+connection.
+.TP
+.B RSCSI
+If the
+.B RSCSI
+environment is present, the remote SCSI server will not be the program
+.B /opt/schily/sbin/rscsi
+but the program pointed to by
+.BR RSCSI .
+Note that the remote SCSI server program name will be ignored if you log in
+using an account that has been created with a remote SCSI server program as
+login shell.
+
+.SH FILES
+.TP
+/etc/wodim.conf
+Default values can be set for the following options in /etc/wodim.conf.
+For example:
+.SM CDR_FIFOSIZE=8m
+or
+.SM CDR_SPEED=2
+.RS
+.TP
+CDR_DEVICE
+This may either hold a device identifier that is suitable to the open
+call of the SCSI transport library or a label in the file /etc/wodim.conf
+that allows to identify a specific drive on the system.
+.TP
+CDR_SPEED
+Sets the default speed value for writing (see also
+.B speed=
+option).
+.TP
+CDR_FIFOSIZE
+Sets the default size of the FIFO (see also
+.BR fs= #
+option).
+.TP
+CDR_MAXFIFOSIZE
+Sets the maximum size of the FIFO (see also
+.BR fs= #
+option).
+.TP
+Any other keyword (label) is an identifier (symbolic name) for a specific drive
+on the system. Such an identifier may not contain the characters ',', '/', '@'
+or ':'.
+.sp
+Each line that follows a label contains a whitespace separated list of items.
+Currently, four items are recognized: the drive's target specification, the
+default speed that should be used for this drive, the default FIFO size
+that should be used for this drive and drive specific options. The values for
+.I speed
+and
+.I fifosize
+may be set to -1 to tell wodim to use the global defaults.
+.I target
+can be -1 to use the auto-guessing of the drive (see above).
+
+The value for driveropts may be omitted or set to "" if no driveropts are used.
+A typical line may look this way:
+.sp
+plex760= 0,5,0 12 50m varirec=1
+.sp
+pioneer= /dev/hdd -1 -1
+.sp
+This tells
+.B wodim
+that a drive named
+.I plex760
+is at scsibus 0, target 5, lun 0 and should be used with speed 12 and
+a FIFO size of 50 MB. It also uses some device specific parameter.
+A second drive may is accessible via the device file /dev/hdd and uses the
+default speed and the default FIFO size.
+.RE
+
+.SH SEE ALSO
+.BR icedax (1),
+.BR readom (1),
+.BR genisoimage (1),
+.BR ssh (1).
+
+.SH NOTES
+.PP
+On Solaris you need to stop the volume management if you like to use the USCSI
+fallback SCSI transport code. Even things like
+.B "wodim -scanbus
+will not work if the volume management is running.
+.PP
+Disks made in
+.B "Track At Once
+mode are not suitable as a master for direct mass production by CD manufacturers.
+You will need the
+.B "disk at once
+option to record such disks.
+Nevertheless the disks made in
+.B "Track At Once
+will normally be read in all CD players. Some old
+audio CD players however may produce a two second click between two audio tracks.
+.PP
+The minimal size of a track is 4 seconds or 300 sectors. If you write
+smaller tracks, the CD-Recorder will add dummy blocks. This is not an
+error, even though the SCSI-error message looks this way.
+.PP
+The Yamaha CDR-400 and all new SCSI-3/mmc conforming drives are supported
+in single and multi-session.
+.PP
+You should run several tests in all supported speeds of your drive with the
+.B \-dummy
+option turned on if you are using
+.B wodim
+on an unknown system. Writing a CD is a real-time process.
+.B NFS, CIFS
+and other network file systems
+won't always deliver constantly the needed data rates.
+If you want to use
+.B wodim
+with CD-images that are located on a
+.B NFS
+mounted filesystem, be sure that the FIFO size is big enough.
+If you want to make sure that buffer underruns are not
+caused by your source disk, you may use the command
+.PP
+.B " wodim -dummy dev=2,0 padsize=600m /dev/null
+.PP
+to create a disk that is entirely made of dummy data.
+.PP
+There are also cases where you either need to be root or install
+.B wodim
+executable with suid-root permissions. First, if you are using a device
+manufactured before 1999 which requires a non-MMC driver, you should run
+.B wodim
+in dummy mode before writing data. If you find a problem doing this, please
+report it to the
+.B cdrkit
+maintainers (see below).
+.PP
+Second, certain functionality may be unusable because of Linux's SCSI
+command filtering. When using
+.B wodim
+for anything except of pure data writing, you should also test the process in
+dummy mode and report trouble to the contact address below.
+.PP
+If you still want to run
+.B wodim
+with root permissions, you can set the permissions of the executable to
+suid-root. See the additional notes of your system/program distribution or
+README.suidroot which is part of the cdrkit source.
+.PP
+You should not connect old drives that do not support
+disconnect/reconnect to either the SCSI bus that is connected to the
+CD-Recorder or the source disk.
+.PP
+A Compact Disc can have no more than 99 tracks.
+.PP
+When creating a disc with both audio and data tracks,
+the data should be on track 1 otherwise you should create
+a CDplus disk which is a multi session disk with the first session
+containing the audio tracks and the following session containing the data track.
+.PP
+Many operating systems are not able to read more than a single data track, or
+need special software to do so.
+.PP
+If you have more information or SCSI command manuals for currently
+unsupported CD/DVD/BR/HD-DVD-Recorders, please contact the
+.B cdrkit
+maintainers (see below).
+.PP
+Many CD recorders have bugs and often require a firmware update to work
+correctly. If you experience problems which cannot be solved or explained by the
+notes above, please look for instructions on the homepage of the particular
+manufacturer.
+.PP
+Some bugs will force you to power cycle the device or to reboot the machine.
+.PP
+The FIFO percent output is computed just after a block of data has been written
+to the CD/DVD-Recorder. For this reason, there will never be 100% FIFO fill ratio
+while the FIFO is in streaming mode.
+
+.SH DIAGNOSTICS
+.PP
+You have 4 seconds to abort
+.B wodim
+start after you see the message:
+.PP
+Starting to write CD at speed %d in %s mode for %s session.
+In most shells you can do that by pressing Ctrl-C.
+.PP
+A typical error message for a SCSI command looks like:
+.sp
+.RS
+.nf
+wodim: I/O error. test unit ready: scsi sendcmd: no error
+CDB: 00 20 00 00 00 00
+status: 0x2 (CHECK CONDITION)
+Sense Bytes: 70 00 05 00 00 00 00 0A 00 00 00 00 25 00 00 00 00 00
+Sense Key: 0x5 Illegal Request, Segment 0
+Sense Code: 0x25 Qual 0x00 (logical unit not supported) Fru 0x0
+Sense flags: Blk 0 (not valid)
+cmd finished after 0.002s timeout 40s
+.fi
+.RE
+.sp
+The first line gives information about the transport of the command.
+The text after the first colon gives the error text for the system call
+from the view of the kernel. It usually is:
+.B "I/O error
+unless other problems happen. The next words contain a short description for
+the SCSI command that fails. The rest of the line tells you if there were
+any problems for the transport of the command over the SCSI bus.
+.B "fatal error
+means that it was not possible to transport the command (i.e. no device present
+at the requested SCSI address).
+.PP
+The second line prints the SCSI command descriptor block for the failed command.
+.PP
+The third line gives information on the SCSI status code returned by the
+command, if the transport of the command succeeds.
+This is error information from the SCSI device.
+.PP
+The fourth line is a hex dump of the auto request sense information for the
+command.
+.PP
+The fifth line is the error text for the sense key if available, followed
+by the segment number that is only valid if the command was a
+.I copy
+command. If the error message is not directly related to the current command,
+the text
+.I deferred error
+is appended.
+.PP
+The sixth line is the error text for the sense code and the sense qualifier if available.
+If the type of the device is known, the sense data is decoded from tables
+in
+.IR scsierrs.c " .
+The text is followed by the error value for a field replaceable unit.
+.PP
+The seventh line prints the block number that is related to the failed command
+and text for several error flags. The block number may not be valid.
+.PP
+The eight line reports the timeout set up for this command and the time
+that the command really needed to complete.
+.PP
+The following message is not an error:
+.sp
+.RS
+.nf
+Track 01: Total bytes read/written: 2048/2048 (1 sectors).
+wodim: I/O error. flush cache: scsi sendcmd: no error
+CDB: 35 00 00 00 00 00 00 00 00 00
+status: 0x2 (CHECK CONDITION)
+Sense Bytes: F0 00 05 80 00 00 27 0A 00 00 00 00 B5 00 00 00 00 00
+Sense Key: 0x5 Illegal Request, Segment 0
+Sense Code: 0xB5 Qual 0x00 (dummy data blocks added) Fru 0x0
+Sense flags: Blk -2147483609 (valid)
+cmd finished after 0.002s timeout 40s
+.fi
+.RE
+.sp
+It simply notifies, that a track that is smaller than the minimum size has been
+expanded to 300 sectors.
+.SH BUGS
+.PP
+.B netscsid
+does not work properly and is generally unmaintained. It is probably not
+compatible with rscsi from
+.BR cdrtools
+either. Good bugfixes are welcome, talk to Cdrkit maintainers.
+.PP
+.B cuefile support is very limited, only one file is allowed. For volunteers,
+see TODO file in the source.
+.PP
+.B Specifying an audio file multiple times causes corruption of the second
+track (effectively no data plus minimum padding).
+.PP
+Some of the bugs may be fixed in Joerg Schilling's cdrtools. See there for
+details, URL attached below.
+
+.SH CREDITS
+.PP
+.TP 15
+Joerg Schilling (schilling@fokus.fhg.de)
+.br
+For writing cdrecord and libscg which represent the most parts of wodim's code.
+.PP
+.TP 15
+Bill Swartz (Bill_Swartz@twolf.com)
+.br
+For helping me with the TEAC driver support
+.TP
+Aaron Newsome (aaron.d.newsome@wdc.com)
+.br
+For letting me develop Sony support on his drive
+.TP
+Eric Youngdale (eric@andante.jic.com)
+.br
+For supplying mkisofs
+.TP
+Gadi Oxman (gadio@netvision.net.il)
+.br
+For tips on the ATAPI standard
+.TP
+Finn Arne Gangstad (finnag@guardian.no)
+.br
+For the first FIFO implementation.
+.TP
+Dave Platt (dplatt@feghoot.ml.org)
+.br
+For creating the experimental packet writing support,
+the first implementation of CD-RW blanking support,
+the first .wav file decoder
+and many nice discussions on cdrecord.
+.TP
+Chris P. Ross (cross@eng.us.uu.net)
+.br
+For the first implementation of a BSDI SCSI transport.
+.TP
+Grant R. Guenther (grant@torque.net)
+.br
+For creating the first parallel port transport implementation
+for Linux.
+.TP
+Kenneth D. Merry (ken@kdm.org)
+.br
+for providing the CAM port for FreeBSD together with Michael Smith (msmith@freebsd.org)
+.TP
+Heiko Ei\*sfeldt (heiko@hexco.de)
+for making libedc_ecc available (needed to write RAW data sectors).
+
+.SH "MAILING LISTS
+If you want to actively take part on the development of wodim,
+you may join the developer mailing list via this URL:
+.sp
+.B
+https://alioth.debian.org/mail/?group_id=31006
+.PP
+The mail address of the list is:
+.B
+debburn-devel@lists.alioth.debian.org
+
+.SH AUTHORS
+.B wodim
+is currently maintained as part of the cdrkit project by its developers. Most of the code and this manual page was originally written by:
+.PP
+.nf
+Joerg Schilling
+Seestr. 110
+D-13353 Berlin
+Germany
+.fi
+.PP
+This application is derived from "cdrecord" as included
+in the cdrtools package [1] created by Joerg
+Schilling, who deserves most of the credit for its success. However, he is not
+involved into the development of this spinoff and therefore he shall not be
+held responsible for any problems caused by it. Do not refer to this application
+as "cdrecord", do not try to get support for wodim by contacting the original
+authors.
+.PP
+Additional information can be found on:
+.br
+https://alioth.debian.org/projects/debburn/
+.PP
+If you have support questions, send them to
+.PP
+.B
+debburn-devel@lists.alioth.debian.org
+.br
+.PP
+If you have definitely found a bug, send a mail to this list or to
+.PP
+.B
+submit@bugs.debian.org
+.br
+.PP
+writing at least a short description into the Subject and "Package: cdrkit" in the first line of the mail body.
+.SH SOURCES
+.PP
+.br
+[1] Cdrtools 2.01.01a08 from May 2006, http://cdrecord.berlios.de
+
diff --git a/wodim/wodim.c b/wodim/wodim.c
new file mode 100644
index 0000000..7a54ccc
--- /dev/null
+++ b/wodim/wodim.c
@@ -0,0 +1,4701 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/*
+ *
+ * Modified by Eduard Bloch in 08/2006 and later
+ */
+
+/* @(#)cdrecord.c 1.310 06/02/09 Copyright 1995-2006 J. Schilling */
+/*
+ * Record data on a CD/CVD-Recorder
+ *
+ * Copyright (c) 1995-2006 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <fctldefs.h>
+#include <errno.h>
+#include <timedefs.h>
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h> /* for rlimit */
+#endif
+#include <statdefs.h>
+#include <unixstd.h>
+#ifdef HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#include <strdefs.h>
+#include <utypes.h>
+#include <intcvt.h>
+#include <signal.h>
+#include <schily.h>
+#include <string.h>
+#include <getargs.h>
+#ifdef HAVE_PRIV_H
+#include <priv.h>
+#endif
+
+#include "xio.h"
+
+#include <usal/scsireg.h> /* XXX wegen SC_NOT_READY */
+#include <usal/scsitransp.h>
+#include <usal/usalcmd.h> /* XXX fuer read_buffer */
+#include "scsi_scan.h"
+
+#include "auheader.h"
+#include "wodim.h"
+#include "defaults.h"
+#include "movesect.h"
+
+#ifdef __linux__
+#include <sys/capability.h> /* for rawio capability */
+#endif
+
+#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0
+#ifdef HAVE_SYS_PRIOCNTL_H /* The preferred SYSvR4 schduler */
+#else
+#define USE_POSIX_PRIORITY_SCHEDULING
+#endif
+#endif
+
+/*
+ * Map toc/track types into names.
+ */
+char *toc2name[] = {
+ "CD-DA",
+ "CD-ROM",
+ "CD-ROM XA mode 1",
+ "CD-ROM XA mode 2",
+ "CD-I",
+ "Illegal toc type 5",
+ "Illegal toc type 6",
+ "Illegal toc type 7",
+};
+
+/*
+ * Map sector types into names.
+ */
+char *st2name[] = {
+ "Illegal sector type 0",
+ "CD-ROM mode 1",
+ "CD-ROM mode 2",
+ "Illegal sector type 3",
+ "CD-DA without preemphasis",
+ "CD-DA with preemphasis",
+ "Illegal sector type 6",
+ "Illegal sector type 7",
+};
+
+/*
+ * Map data block types into names.
+ */
+char *db2name[] = {
+ "Raw (audio)",
+ "Raw (audio) with P/Q sub channel",
+ "Raw (audio) with P/W packed sub channel",
+ "Raw (audio) with P/W raw sub channel",
+ "Reserved mode 4",
+ "Reserved mode 5",
+ "Reserved mode 6",
+ "Vendor unique mode 7",
+ "CD-ROM mode 1",
+ "CD-ROM mode 2",
+ "CD-ROM XA mode 2 form 1",
+ "CD-ROM XA mode 2 form 1 (with subheader)",
+ "CD-ROM XA mode 2 form 2",
+ "CD-ROM XA mode 2 form 1/2/mix",
+ "Reserved mode 14",
+ "Vendor unique mode 15",
+};
+
+/*
+ * Map write modes into names.
+ */
+static char wm_none[] = "unknown";
+static char wm_ill[] = "illegal";
+
+char *wm2name[] = {
+ wm_none,
+ "BLANK",
+ "FORMAT",
+ wm_ill,
+ "PACKET",
+ wm_ill,
+ wm_ill,
+ wm_ill,
+ "TAO",
+ wm_ill,
+ wm_ill,
+ wm_ill,
+ "SAO",
+ "SAO/RAW16", /* Most liklely not needed */
+ "SAO/RAW96P",
+ "SAO/RAW96R",
+ "RAW",
+ "RAW/RAW16",
+ "RAW/RAW96P",
+ "RAW/RAW96R",
+};
+
+int debug; /* print debug messages */
+static int kdebug; /* print kernel debug messages */
+static int scsi_verbose; /* SCSI verbose flag */
+static int silent; /* SCSI silent flag */
+int lverbose; /* static verbose flag */
+int xdebug; /* extended debug flag */
+
+char *buf; /* The transfer buffer */
+long bufsize = -1; /* The size of the transfer buffer */
+
+static int gracetime = GRACE_TIME;
+static int raw_speed = -1;
+static int dma_speed = -1;
+static int dminbuf = -1; /* XXX Hack for now drive min buf fill */
+BOOL isgui;
+static int didintr;
+char *driveropts;
+static char *cuefilename = NULL;
+static uid_t oeuid = (uid_t)-1;
+
+struct timeval starttime;
+struct timeval wstarttime;
+struct timeval stoptime;
+struct timeval fixtime;
+
+static long fs = -1; /* fifo (ring buffer) size */
+static Llong warn_minisize = -1L;
+
+static int gracewait(cdr_t *dp, BOOL *didgracep);
+static void cdrstats(cdr_t *dp);
+static void susage(int);
+static void usage(int);
+static void blusage(int);
+static void formattypeusage(int);
+static void intr(int sig);
+static void catchsig(int sig);
+static int scsi_cb(void *arg);
+static void intfifo(int sig);
+static void exscsi(int excode, void *arg);
+static void excdr(int excode, void *arg);
+int read_buf(int f, char *bp, int size);
+int fill_buf(int f, track_t *trackp, long secno, char *bp, int size);
+int get_buf(int f, track_t *trackp, long secno, char **bpp, int size);
+int write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt,
+ int secspt, BOOL islast);
+static int write_track_data(SCSI *usalp, cdr_t *, track_t *);
+int pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp, long startsec,
+ Llong amt, BOOL dolast, Llong *bytesp);
+int write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp,
+ long startsec, Llong amt, int secsize, BOOL dolast,
+ Llong *bytesp);
+static void printdata(int, track_t *);
+static void printaudio(int, track_t *);
+static void checkfile(int, track_t *);
+static int checkfiles(int, track_t *);
+static void setleadinout(int, track_t *);
+static void setpregaps(int, track_t *);
+static long checktsize(int, track_t *);
+static void opentracks(track_t *);
+static void checksize(track_t *);
+static BOOL checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags);
+static void raise_fdlim(void);
+static void raise_memlock(void);
+static int gargs(int, char **, int *, track_t *, char **, int *, cdr_t **,
+ int *, long *, int *, int *);
+static void set_trsizes(cdr_t *, int, track_t *);
+void load_media(SCSI *usalp, cdr_t *, BOOL);
+void unload_media(SCSI *usalp, cdr_t *, int);
+void reload_media(SCSI *usalp, cdr_t *);
+void set_secsize(SCSI *usalp, int secsize);
+static int get_dmaspeed(SCSI *usalp, cdr_t *);
+static BOOL do_opc(SCSI *usalp, cdr_t *, int);
+static void check_recovery(SCSI *usalp, cdr_t *, int);
+void audioread(SCSI *usalp, cdr_t *, int);
+static void print_msinfo(SCSI *usalp, cdr_t *);
+static void print_toc(SCSI *usalp, cdr_t *);
+static void print_track(int, long, struct msf *, int, int, int);
+#if !defined(HAVE_SYS_PRIOCNTL_H)
+static int rt_raisepri(int);
+#endif
+void raisepri(int);
+static void wait_input(void);
+static void checkgui(void);
+static int getbltype(char *optstr, long *typep);
+static int getformattype(char *optstr, long *typep);
+static void print_drflags(cdr_t *dp);
+static void print_wrmodes(cdr_t *dp);
+static BOOL check_wrmode(cdr_t *dp, int wmode, int tflags);
+static void set_wrmode(cdr_t *dp, int wmode, int tflags);
+static void linuxcheck(void);
+
+#ifdef __linux__
+static int get_cap(cap_value_t cap_array);
+#endif
+
+struct exargs {
+ SCSI *usalp;
+ cdr_t *dp;
+ int old_secsize;
+ int flags;
+ int exflags;
+} exargs;
+
+void fifo_cleanup(void) {
+ kill_faio();
+}
+
+/* shared variables */
+int scandevs = 0;
+char *msifile = NULL;
+
+int main(int argc, char *argv[])
+{
+ char *dev = NULL;
+ int timeout = 40; /* Set default timeout to 40s CW-7502 is slow*/
+ int speed = -1;
+ long flags = 0L;
+ int blanktype = 0;
+ int formattype = 0;
+ int i;
+ int tracks = 0;
+ int trackno;
+ long tsize;
+ track_t track[MAX_TRACK+2]; /* Max tracks + track 0 + track AA */
+ cdr_t *dp = (cdr_t *)0;
+ long startsec = 0L;
+ int errs = 0;
+ SCSI *usalp = NULL;
+ char errstr[80];
+ BOOL gracedone = FALSE;
+ int ispacket;
+ BOOL is_cdwr = FALSE;
+ BOOL is_dvdwr = FALSE;
+
+ buf=strstr(argv[0], "cdrecord");
+ if(buf && '\0' == buf[8]) /* lame cheater detected */
+ argv[0]="wodim";
+
+#ifdef __EMX__
+ /* This gives wildcard expansion with Non-Posix shells with EMX */
+ _wildcard(&argc, &argv);
+#endif
+ save_args(argc, argv);
+ oeuid = geteuid(); /* Remember saved set uid */
+
+ fillbytes(track, sizeof (track), '\0');
+ for (i = 0; i < MAX_TRACK+2; i++)
+ track[i].track = track[i].trackno = i;
+ track[0].tracktype = TOC_MASK;
+ raise_fdlim();
+ ispacket = gargs(argc, argv, &tracks, track, &dev, &timeout, &dp, &speed, &flags,
+ &blanktype, &formattype);
+ if ((track[0].tracktype & TOC_MASK) == TOC_MASK)
+ comerrno(EX_BAD, "Internal error: Bad TOC type.\n");
+
+ if (flags & F_VERSION) {
+ fprintf(stderr,
+ "Cdrecord-yelling-line-to-tell-frontends-to-use-it-like-version 2.01.01a03-dvd \n"
+ "Wodim " CDRKIT_VERSION "\n"
+ "Copyright (C) 2006 Cdrkit suite contributors\n"
+ "Based on works from Joerg Schilling, Copyright (C) 1995-2006, J. Schilling\n"
+ );
+ exit(0);
+ }
+
+ checkgui();
+
+ if (debug || lverbose) {
+ printf("TOC Type: %d = %s\n",
+ track[0].tracktype & TOC_MASK,
+ toc2name[track[0].tracktype & TOC_MASK]);
+ }
+
+ if ((flags & (F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_INQUIRY|F_SCANBUS|F_RESET)) == 0) {
+ /*
+ * Try to lock us im memory (will only work for root)
+ * but you need access to root anyway to send SCSI commands.
+ * We need to be root to open /dev/usal? or similar devices
+ * on other OS variants and we need to be root to be able
+ * to send SCSI commands at least on AIX and
+ * Solaris (USCSI only) regardless of the permissions for
+ * opening files
+ *
+ * XXX The following test used to be
+ * XXX #if defined(HAVE_MLOCKALL) || defined(_POSIX_MEMLOCK)
+ * XXX but the definition for _POSIX_MEMLOCK did change during
+ * XXX the last 8 years and the autoconf test is better for
+ * XXX the static case. sysconf() only makes sense if we like
+ * XXX to check dynamically.
+ */
+ raise_memlock();
+#if defined(HAVE_MLOCKALL)
+ /*
+ * XXX mlockall() needs root privilleges.
+ */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
+ if(lverbose>2)
+ fprintf(stderr,
+ "W: Cannot do mlockall(2). Possibly increased risk for buffer underruns.\n");
+ }
+#endif
+
+ /*
+ * XXX raisepri() needs root privilleges.
+ */
+ raisepri(0); /* max priority */
+ /*
+ * XXX shmctl(id, SHM_LOCK, 0) needs root privilleges.
+ * XXX So if we use SysV shared memory, wee need to be root.
+ *
+ * Note that not being able to set up a FIFO bombs us
+ * back to the DOS ages. Trying to run cdrecord without
+ * root privillegs is extremely silly, it breaks most
+ * of the advanced features. We need to be at least installed
+ * suid root or called by RBACs pfexec.
+ */
+ init_fifo(fs); /* Attach shared memory (still one process) */
+ }
+
+ if ((flags & F_WAITI) != 0) {
+ if (lverbose)
+ printf("Waiting for data on stdin...\n");
+ wait_input();
+ }
+
+ /*
+ * Call usal_remote() to force loading the remote SCSI transport library
+ * code that is located in librusal instead of the dummy remote routines
+ * that are located inside libusal.
+ */
+ usal_remote();
+ if (dev != NULL &&
+ ((strncmp(dev, "HELP", 4) == 0) ||
+ (strncmp(dev, "help", 4) == 0))) {
+ usal_help(stderr);
+ exit(0);
+ }
+
+ if( (!dev || *dev=='\0'|| 0==strcmp(dev, "-1")) && (flags & F_SCANBUS)==0 ) {
+ int64_t need_size=0L;
+ struct stat statbuf;
+ int t;
+
+ fprintf(stderr, "Device was not specified. Trying to find an appropriate drive...\n");
+
+ /* estimate how much data user wants to write */
+ for(t=1;t<=tracks;t++) {
+ if(track[t].tracksize>=0)
+ need_size+=track[t].tracksize;
+ else if(0==stat(track[t].filename, &statbuf))
+ need_size+=statbuf.st_size;
+ }
+ usalp=open_auto(need_size, debug, lverbose);
+ }
+
+ if(!usalp)
+ usalp = usal_open(dev, errstr, sizeof(errstr), debug, lverbose);
+
+ if(!usalp)
+ {
+ errmsg("\nCannot open SCSI driver!\n"
+ "For possible targets try 'wodim --devices' or 'wodim -scanbus'.\n"
+ "For possible transport specifiers try 'wodim dev=help'.\n"
+ "For IDE/ATAPI devices configuration, see the file README.ATAPI.setup from\n"
+ "the wodim documentation.\n");
+ exit(EX_BAD);
+ }
+
+#ifdef HAVE_PRIV_SET
+#ifdef PRIV_DEBUG
+ fprintf(stderr, "file_dac_read: %d\n", priv_ineffect(PRIV_FILE_DAC_READ));
+#endif
+ /*
+ * Give up privs we do not need anymore.
+ * We no longer need:
+ * file_dac_read,proc_lock_memory,proc_priocntl,net_privaddr
+ * We still need:
+ * sys_devices
+ */
+ priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
+ PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
+ priv_set(PRIV_OFF, PRIV_PERMITTED,
+ PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
+ PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, NULL);
+ priv_set(PRIV_OFF, PRIV_INHERITABLE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_LOCK_MEMORY,
+ PRIV_PROC_PRIOCNTL, PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
+#endif
+ /*
+ * This is only for OS that do not support fine grained privs.
+ *
+ * XXX Below this point we do not need root privilleges anymore.
+ */
+ if (geteuid() != getuid()) { /* AIX does not like to do this */
+ /* If we are not root */
+#ifdef HAVE_SETREUID
+ if (setreuid(-1, getuid()) < 0)
+#else
+#ifdef HAVE_SETEUID
+ if (seteuid(getuid()) < 0)
+#else
+ if (setuid(getuid()) < 0)
+#endif
+#endif
+ comerr("Panic cannot set back effective uid.\n");
+ }
+
+#ifdef __linux__
+ /* get the rawio capability */
+ if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2))
+ {
+ perror("Warning: Cannot gain SYS_RAWIO capability");
+ fprintf(stderr, "Possible reason: wodim not installed SUID root.\n");
+ }
+#endif
+
+ /*
+ * WARNING: We now are no more able to do any privilleged operation
+ * unless we have been called by root.
+ *
+ * XXX It may be that we later get problems in init_faio() because
+ * XXX this function calls raisepri() to lower the priority slightly.
+ */
+ usal_settimeout(usalp, timeout);
+ usalp->verbose = scsi_verbose;
+ usalp->silent = silent;
+ usalp->debug = debug;
+ usalp->kdebug = kdebug;
+ usalp->cap->c_bsize = DATA_SEC_SIZE;
+
+ if ((flags & F_MSINFO) == 0 || lverbose) {
+ char *vers;
+ char *auth;
+
+
+ if(lverbose)
+ fprintf(stderr, "Wodim version: " CDRKIT_VERSION "\n");
+
+ vers = usal_version(0, SCG_VERSION);
+ auth = usal_version(0, SCG_AUTHOR);
+ if(lverbose >1 && auth && vers)
+ fprintf(stderr, "Using libusal version '%s-%s'.\n", auth, vers);
+
+
+ vers = usal_version(usalp, SCG_RVERSION);
+ auth = usal_version(usalp, SCG_RAUTHOR);
+ if (lverbose > 1 && vers && auth)
+ fprintf(stderr, "Using remote transport code version '%s-%s'\n", auth, vers);
+ }
+
+ if (lverbose && driveropts)
+ printf("Driveropts: '%s'\n", driveropts);
+
+/* bufsize = usal_bufsize(usalp, CDR_BUF_SIZE);*/
+ bufsize = usal_bufsize(usalp, bufsize);
+ if (lverbose || debug)
+ fprintf(stderr, "SCSI buffer size: %ld\n", bufsize);
+ if ((buf = usal_getbuf(usalp, bufsize)) == NULL)
+ comerr("Cannot get SCSI I/O buffer.\n");
+
+ if (scandevs)
+ return (list_devices(usalp, stdout, 0));
+
+ if ((flags & F_SCANBUS) != 0) {
+ select_target(usalp, stdout);
+ exit(0);
+ }
+ if ((flags & F_RESET) != 0) {
+ if (usal_reset(usalp, SCG_RESET_NOP) < 0)
+ comerr("Cannot reset (OS does not implement reset).\n");
+ if (usal_reset(usalp, SCG_RESET_TGT) >= 0)
+ exit(0);
+ if (usal_reset(usalp, SCG_RESET_BUS) < 0)
+ comerr("Cannot reset target.\n");
+ exit(0);
+ }
+
+ /*
+ * First try to check which type of SCSI device we
+ * have.
+ if (debug || lverbose)
+ printf("atapi: %d\n", usal_isatapi(usalp));
+ */
+ usalp->silent++;
+ test_unit_ready(usalp); /* eat up unit attention */
+ usalp->silent--;
+ if (!do_inquiry(usalp, (flags & F_MSINFO) == 0 || lverbose)) {
+ errmsgno(EX_BAD, "Cannot do inquiry for CD/DVD-Recorder.\n");
+ if (unit_ready(usalp))
+ errmsgno(EX_BAD, "The unit seems to be hung and needs power cycling.\n");
+ exit(EX_BAD);
+ }
+#ifdef GCONF
+ /*
+ * Debug only
+ */
+ {
+ extern void gconf(SCSI *);
+
+ if (lverbose > 2)
+ gconf(usalp);
+ }
+#endif
+
+ if ((flags & F_PRCAP) != 0) {
+ print_capabilities(usalp);
+ print_capabilities_mmc4(usalp);
+ exit(0);
+ }
+ if ((flags & F_INQUIRY) != 0)
+ exit(0);
+
+ if (dp == (cdr_t *)NULL) { /* No driver= option specified */
+ dp = get_cdrcmds(usalp); /* Calls dp->cdr_identify() */
+ }
+ else if (!is_unknown_dev(usalp) && dp != get_cdrcmds(usalp)) {
+ errmsgno(EX_BAD, "WARNING: Trying to use other driver on known device.\n");
+ }
+ is_mmc(usalp, &is_cdwr, &is_dvdwr);
+ if (ispacket) {
+ if (is_dvdwr) {
+ track[0].flags |= TI_PACKET;
+ /*XXX put here to only affect DVD writing, should be in gargs.
+ * however if set in args for all mode, packet writing is then
+ * broken for all disc as cdrecord assume that PACKET imply TAO which
+ * is not true at all???? */
+ track[0].flags &= ~TI_TAO;
+ }
+ }
+
+ if (dp == (cdr_t *)0)
+ comerrno(EX_BAD, "Sorry, no supported CD/DVD-Recorder found on this target.\n");
+
+ /* DVD does not support TAO */
+ if (dp->is_dvd) {
+ if(lverbose>1)
+ fprintf(stderr, "Using Session At Once (SAO) for DVD mode.\n");
+ dp->cdr_flags |= F_SAO;
+ for (i = 0; i <= MAX_TRACK; i++) {
+ track[i].flags &= ~TI_TAO;
+ track[i].flags |= TI_SAO;
+ }
+ }
+
+ if (!is_cddrive(usalp))
+ comerrno(EX_BAD, "Sorry, no CD/DVD-Drive found on this target.\n");
+ /*
+ * The driver is known, set up data structures...
+ */
+ {
+ cdr_t *ndp;
+ dstat_t *dsp;
+
+ ndp = malloc(sizeof (cdr_t));
+ dsp = malloc(sizeof (dstat_t));
+ if (ndp == NULL || dsp == NULL)
+ comerr("Cannot allocate memory for driver structure.\n");
+ movebytes(dp, ndp, sizeof (cdr_t));
+ dp = ndp;
+ dp->cdr_flags |= CDR_ALLOC;
+ dp->cdr_cmdflags = flags;
+
+ fillbytes(dsp, sizeof (*dsp), '\0');
+ dsp->ds_minbuf = 0xFFFF;
+ dp->cdr_dstat = dsp;
+ }
+
+ if ((flags & (F_MSINFO|F_TOC|F_LOAD|F_DLCK|F_EJECT)) == 0 ||
+ tracks > 0 ||
+ cuefilename != NULL)
+ {
+ if ((dp->cdr_flags & CDR_ISREADER) != 0) {
+ errmsgno(EX_BAD,
+ "Sorry, no CD/DVD-Recorder or unsupported CD/DVD-Recorder found on this target.\n");
+ }
+
+ if (!is_mmc(usalp, &is_cdwr, &is_dvdwr))
+ is_cdwr = TRUE; /* If it is not MMC, it must be a CD writer */
+
+ if (is_dvdwr && !set_cdrcmds("mmc_mdvd", (cdr_t **)NULL)) {
+ errmsgno(EX_BAD,
+ "Internal error, DVD driver failure. Please report to debburn-devel@lists.alioth.debian.org.\n");
+ }
+ /*
+ * Only exit if this is not the ProDVD test binary.
+ */
+ if (!is_cdwr)
+ exit(EX_BAD);
+ }
+
+ /*
+ * Set up data structures for current drive state.
+ */
+ if ((*dp->cdr_attach)(usalp, dp) != 0)
+ comerrno(EX_BAD, "Cannot attach driver for CD/DVD-Recorder.\n");
+
+ if (lverbose > 1) {
+ printf("Drive current speed: %d\n", dp->cdr_dstat->ds_dr_cur_wspeed);
+ printf("Drive default speed: %d\n", dp->cdr_speeddef);
+ printf("Drive max speed : %d\n", dp->cdr_speedmax);
+ }
+ if (speed > (int)dp->cdr_speedmax && (flags & F_FORCE) == 0)
+ speed = dp->cdr_speedmax;
+ if (speed < 0)
+ speed = dp->cdr_speeddef;
+
+ if (lverbose > 1) {
+ printf("Selected speed : %d\n", speed);
+ }
+ dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
+
+ exargs.usalp = usalp;
+ exargs.dp = dp;
+ exargs.old_secsize = -1;
+ exargs.flags = flags;
+
+ if ((flags & F_MSINFO) == 0 || lverbose) {
+ printf("Using %s (%s).\n", dp->cdr_drtext, dp->cdr_drname);
+ print_drflags(dp);
+ print_wrmodes(dp);
+ }
+ usalp->silent++;
+ if ((debug || lverbose)) {
+ tsize = -1;
+ if ((*dp->cdr_buffer_cap)(usalp, &tsize, (long *)0) < 0 || tsize < 0) {
+ if (read_buffer(usalp, buf, 4, 0) >= 0)
+ tsize = a_to_u_4_byte(buf);
+ }
+ if (tsize > 0) {
+ printf("Drive buf size : %lu = %lu KB\n",
+ tsize, tsize >> 10);
+ }
+ }
+ usalp->silent--;
+
+ dma_speed = get_dmaspeed(usalp, dp);
+
+ if ((debug || lverbose) && dma_speed > 0) {
+ /*
+ * We do not yet know what medium type is in...
+ */
+ printf("Drive DMA Speed: %d kB/s %dx CD %dx DVD\n",
+ dma_speed, dma_speed/176, dma_speed/1385);
+ }
+ if ((tracks > 0 || cuefilename != NULL) && (debug || lverbose))
+ printf("FIFO size : %lu = %lu KB\n", fs, fs >> 10);
+
+#ifdef HAVE_LIB_EDC_ECC
+ if ((flags & F_RAW) != 0 && (dp->cdr_dstat->ds_flags & DSF_DVD) == 0)
+ raw_speed = encspeed(debug || lverbose);
+#endif
+
+ if ((flags & F_CHECKDRIVE) != 0)
+ exit(0);
+
+ if ((flags & F_ABORT) != 0) {
+ /*
+ * flush cache is not supported by CD-ROMs avoid prob with -toc
+ */
+ usalp->silent++;
+ scsi_flush_cache(usalp, FALSE);
+ (*dp->cdr_abort_session)(usalp, dp);
+ usalp->silent--;
+ exit(0);
+ }
+
+ if (tracks == 0 && cuefilename == NULL &&
+ (flags & (F_FIX|F_BLANK)) == 0 && (flags & F_EJECT) != 0) {
+ /*
+ * Do not check if the unit is ready here to allow to open
+ * an empty unit too.
+ */
+ unload_media(usalp, dp, flags);
+ exit(0);
+ }
+ flush();
+
+ if (cuefilename) {
+ parsecue(cuefilename, track);
+ tracks = track[0].tracks;
+ } else {
+ opentracks(track);
+ }
+
+ if (tracks > 1)
+ sleep(2); /* Let the user watch the inquiry messages */
+
+ if (tracks > 0 && !check_wrmode(dp, flags, track[1].flags))
+ comerrno(EX_BAD, "Illegal write mode for this drive.\n");
+
+ if ((track[0].flags & TI_TEXT) == 0 && /* CD-Text not yet processed */
+ (track[MAX_TRACK+1].flags & TI_TEXT) != 0) {
+ /*
+ * CD-Text from textfile= or from CUE CDTEXTFILE will win
+ * over CD-Text from *.inf files and over CD-Text from
+ * CUE SONGWRITER, ...
+ */
+ packtext(tracks, track);
+ track[0].flags |= TI_TEXT;
+ }
+#ifdef CLONE_WRITE
+ if (flags & F_CLONE) {
+ clone_toc(track);
+ clone_tracktype(track);
+ }
+#endif
+ setleadinout(tracks, track);
+ set_trsizes(dp, tracks, track);
+ setpregaps(tracks, track);
+ checkfiles(tracks, track);
+ tsize = checktsize(tracks, track);
+
+ /*
+ * Make wm2name[wrmode] work.
+ * This must be done after the track flags have been set up
+ * by the functions above.
+ */
+ if (tracks == 0 && (flags & F_BLANK) != 0)
+ dp->cdr_dstat->ds_wrmode = WM_BLANK;
+ else if (tracks == 0 && (flags & F_FORMAT) != 0)
+ dp->cdr_dstat->ds_wrmode = WM_FORMAT;
+ else
+ set_wrmode(dp, flags, track[1].flags);
+
+ /*
+ * Debug only
+ */
+ {
+ void *cp = NULL;
+
+ (*dp->cdr_gen_cue)(track, &cp, FALSE);
+ if (cp)
+ free(cp);
+ }
+
+ /*
+ * Create Lead-in data. Only needed in RAW mode.
+ */
+ do_leadin(track);
+
+
+ /*
+ * Install exit handler before we change the drive status.
+ */
+ on_comerr(exscsi, &exargs);
+
+ if ((flags & F_FORCE) == 0)
+ load_media(usalp, dp, TRUE);
+
+ if ((flags & (F_LOAD|F_DLCK)) != 0) {
+ if ((flags & F_DLCK) == 0) {
+ usalp->silent++; /* silently */
+ scsi_prevent_removal(
+ usalp, 0); /* allow manual open */
+ usalp->silent--; /* if load failed... */
+ }
+ exit(0); /* we did not change status */
+ }
+ exargs.old_secsize = sense_secsize(usalp, 1);
+ if (exargs.old_secsize < 0)
+ exargs.old_secsize = sense_secsize(usalp, 0);
+ if (debug)
+ printf("Current Secsize: %d\n", exargs.old_secsize);
+ usalp->silent++;
+ if (read_capacity(usalp) < 0) {
+ if (exargs.old_secsize > 0)
+ usalp->cap->c_bsize = exargs.old_secsize;
+ }
+ usalp->silent--;
+ if (exargs.old_secsize < 0)
+ exargs.old_secsize = usalp->cap->c_bsize;
+ if (exargs.old_secsize != usalp->cap->c_bsize)
+ errmsgno(EX_BAD, "Warning: blockdesc secsize %d differs from cap secsize %d\n",
+ exargs.old_secsize, usalp->cap->c_bsize);
+
+ if (lverbose)
+ printf("Current Secsize: %d\n", exargs.old_secsize);
+
+ if (exargs.old_secsize > 0 && exargs.old_secsize != DATA_SEC_SIZE) {
+ /*
+ * Some drives (e.g. Plextor) don't like to write correctly
+ * in SAO mode if the sector size is set to 512 bytes.
+ * In addition, wodim -msinfo will not work properly
+ * if the sector size is not 2048 bytes.
+ */
+ set_secsize(usalp, DATA_SEC_SIZE);
+ }
+
+ /*
+ * Is this the right place to do this ?
+ */
+ check_recovery(usalp, dp, flags);
+
+/*audioread(dp, flags);*/
+/*unload_media(usalp, dp, flags);*/
+/*return 0;*/
+ if (flags & F_WRITE)
+ dp->cdr_dstat->ds_cdrflags |= RF_WRITE;
+ if (flags & F_BLANK)
+ dp->cdr_dstat->ds_cdrflags |= RF_BLANK;
+ if (flags & F_PRATIP || lverbose > 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_PRATIP;
+ }
+ if (flags & F_IMMED || dminbuf > 0) {
+ if (dminbuf <= 0)
+ dminbuf = 50;
+ if (lverbose <= 0) /* XXX Hack needed for now */
+ lverbose++;
+ dp->cdr_dstat->ds_cdrflags |= RF_WR_WAIT;
+ }
+ if ((*dp->cdr_getdisktype)(usalp, dp) < 0) {
+ errmsgno(EX_BAD, "Cannot get disk type.\n");
+ if ((flags & F_FORCE) == 0)
+ comexit(EX_BAD);
+ }
+ if (flags & F_PRATIP) {
+ comexit(0);
+ }
+ /*
+ * The next actions should depend on the disk type.
+ */
+ if (dma_speed > 0) {
+ if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0)
+ dma_speed /= 176;
+ else
+ dma_speed /= 1385;
+ }
+
+ /*
+ * Init drive to default modes:
+ *
+ * We set TAO unconditionally to make checkdsize() work
+ * currectly in SAO mode too.
+ *
+ * At least MMC drives will not return the next writable
+ * address we expect when the drive's write mode is set
+ * to SAO. We need this address for mkisofs and thus
+ * it must be the first user accessible sector and not the
+ * first sector of the pregap.
+ *
+ * XXX The ACER drive:
+ * XXX Vendor_info : 'ATAPI '
+ * XXX Identifikation : 'CD-R/RW 8X4X32 '
+ * XXX Revision : '5.EW'
+ * XXX Will not return from -dummy to non-dummy without
+ * XXX opening the tray.
+ */
+ usalp->silent++;
+ if ((*dp->cdr_init)(usalp, dp) < 0)
+ comerrno(EX_BAD, "Cannot init drive.\n");
+ usalp->silent--;
+
+ if (flags & F_SETDROPTS) {
+ /*
+ * Note that the set speed function also contains
+ * drive option processing for speed related drive options.
+ */
+ if ((*dp->cdr_opt1)(usalp, dp) < 0) {
+ errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n");
+ }
+ if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) {
+ errmsgno(EX_BAD, "Cannot set speed/dummy.\n");
+ }
+ dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
+ if ((*dp->cdr_opt2)(usalp, dp) < 0) {
+ errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n");
+ }
+ comexit(0);
+ }
+ /*
+ * XXX If dp->cdr_opt1() ever affects the result for
+ * XXX the multi session info we would need to move it here.
+ */
+ if (flags & F_MSINFO) {
+ print_msinfo(usalp, dp);
+ comexit(0);
+ }
+ if (flags & F_TOC) {
+ print_toc(usalp, dp);
+ comexit(0);
+ }
+#ifdef XXX
+ if ((*dp->cdr_check_session)() < 0) {
+ comexit(EX_BAD);
+ }
+#endif
+ {
+ Int32_t omb = dp->cdr_dstat->ds_maxblocks;
+
+ if ((*dp->cdr_opt1)(usalp, dp) < 0) {
+ errmsgno(EX_BAD, "Cannot set up 1st set of driver options.\n");
+ }
+ if (tsize > 0 && omb != dp->cdr_dstat->ds_maxblocks) {
+ printf("Disk size changed by user options.\n");
+ printf("Checking disk capacity according to new values.\n");
+ }
+ }
+ if (tsize == 0) {
+ if (tracks > 0) {
+ errmsgno(EX_BAD,
+ "WARNING: Total disk size unknown. Data may not fit on disk.\n");
+ }
+ } else if (tracks > 0) {
+ /*
+ * XXX How do we let the user check the remaining
+ * XXX disk size witout starting the write process?
+ */
+ if (!checkdsize(usalp, dp, tsize, flags))
+ comexit(EX_BAD);
+ }
+ if (tracks > 0 && fs > 0l) {
+#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID)
+ /*
+ * Hack to work around the POSIX design bug in real time
+ * priority handling: we need to be root even to lower
+ * our priority.
+ * Note that we need to find a more general way that works
+ * even on OS that do not support getreuid() which is *BSD
+ * and SUSv3 only.
+ */
+ if (oeuid != getuid()) {
+ if (setreuid(-1, oeuid) < 0)
+ errmsg("Could set back effective uid.\n");
+ }
+
+#endif
+ /*
+ * fork() here to start the extra process needed for
+ * improved buffering.
+ */
+ if (!init_faio(track, bufsize))
+ fs = 0L;
+ else
+ on_comerr(excdr, &exargs);
+
+ atexit(fifo_cleanup);
+
+#if defined(USE_POSIX_PRIORITY_SCHEDULING) && defined(HAVE_SETREUID)
+ /*
+ * XXX Below this point we never need root privilleges anymore.
+ */
+ if (geteuid() != getuid()) { /* AIX does not like to do this */
+ /* If we are not root */
+ if (setreuid(-1, getuid()) < 0)
+ comerr("Panic cannot set back effective uid.\n");
+ }
+#ifdef __linux__
+ if (get_cap(CAP_SYS_RAWIO) && (debug || lverbose>2))
+ perror("Error: Cannot gain SYS_RAWIO capability, is wodim installed SUID root? Reason");
+#endif
+
+#endif
+ }
+
+ if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0) {
+ errmsgno(EX_BAD, "Cannot set speed/dummy.\n");
+ if ((flags & F_FORCE) == 0)
+ comexit(EX_BAD);
+ }
+ dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
+ if ((flags & F_WRITE) != 0 && raw_speed >= 0) {
+ int max_raw = (flags & F_FORCE) != 0 ? raw_speed:raw_speed/2;
+
+ if (getenv("CDR_FORCERAWSPEED"))
+ max_raw = raw_speed;
+
+ for (i = 1; i <= MAX_TRACK; i++) {
+ /*
+ * Check for Clone tracks
+ */
+ if ((track[i].sectype & ST_MODE_RAW) != 0)
+ continue;
+ /*
+ * Check for non-data tracks
+ */
+ if ((track[i].sectype & ST_MODE_MASK) == ST_MODE_AUDIO)
+ continue;
+
+ if (speed > max_raw) {
+ errmsgno(EX_BAD,
+ "Processor too slow. Cannot write RAW data at speed %d.\n",
+ speed);
+ comerrno(EX_BAD, "Max RAW data speed on this processor is %d.\n",
+ max_raw);
+ }
+ break;
+ }
+ }
+ if (tracks > 0 && (flags & F_WRITE) != 0 && dma_speed > 0) {
+ int max_dma = (dma_speed+1)*4/5; /* use an empirical formula to estimate available bandwith */
+
+ if((flags & F_FORCE) != 0 || getenv("CDR_FORCESPEED"))
+ max_dma = dma_speed;
+
+ if (speed > max_dma) {
+ errmsgno(EX_BAD,
+ "DMA speed too slow (OK for %dx). Cannot write at speed %dx.\n",
+ max_dma, speed);
+ if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0) {
+ errmsgno(EX_BAD, "Max DMA data speed is %d.\n", max_dma);
+ comerrno(EX_BAD, "Try to use 'driveropts=burnfree'.\n");
+ }
+ }
+ }
+ if ((flags & (F_WRITE|F_BLANK)) != 0 &&
+ (dp->cdr_dstat->ds_flags & DSF_ERA) != 0) {
+ if (xdebug) {
+ printf("Current speed %d, medium low speed: %d medium high speed: %d\n",
+ speed,
+ dp->cdr_dstat->ds_at_min_speed,
+ dp->cdr_dstat->ds_at_max_speed);
+ }
+ if (dp->cdr_dstat->ds_at_max_speed > 0 &&
+ speed <= 8 &&
+ speed > (int)dp->cdr_dstat->ds_at_max_speed) {
+ /*
+ * Be careful here: 10x media may be written faster.
+ * The current code will work as long as there is no
+ * writer that can only write faster than 8x
+ */
+ if ((flags & F_FORCE) == 0) {
+ errmsgno(EX_BAD,
+ "Write speed %d of medium not sufficient for this writer.\n",
+ dp->cdr_dstat->ds_at_max_speed);
+ comerrno(EX_BAD,
+ "You may have used an ultra low speed medium on a high speed writer.\n");
+ }
+ }
+
+ if ((dp->cdr_dstat->ds_flags & DSF_ULTRASPP_ERA) != 0 &&
+ (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0)) {
+ if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRAP) == 0) {
+ comerrno(EX_BAD,
+ "Trying to use ultra high speed+ medium on a writer which is not\ncompatible with ultra high speed+ media.\n");
+ } else if ((flags & F_FORCE) == 0) {
+ comerrno(EX_BAD,
+ "Probably trying to use ultra high speed+ medium on improper writer.\n");
+ }
+ } else if ((dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA) != 0 &&
+ (speed < 16 || (dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0)) {
+ if ((dp->cdr_cdrw_support & CDR_CDRW_ULTRA) == 0) {
+ comerrno(EX_BAD,
+ "Trying to use ultra high speed medium on a writer which is not\ncompatible with ultra high speed media.\n");
+ } else if ((flags & F_FORCE) == 0) {
+ comerrno(EX_BAD,
+ "Probably trying to use ultra high speed medium on improper writer.\n");
+ }
+ }
+ if (dp->cdr_dstat->ds_at_min_speed >= 4 &&
+ dp->cdr_dstat->ds_at_max_speed > 4 &&
+ dp->cdr_dstat->ds_dr_max_wspeed <= 4) {
+ if ((flags & F_FORCE) == 0) {
+ comerrno(EX_BAD,
+ "Trying to use high speed medium on low speed writer.\n");
+ }
+ }
+ if ((int)dp->cdr_dstat->ds_at_min_speed > speed) {
+ if ((flags & F_FORCE) == 0) {
+ errmsgno(EX_BAD,
+ "Write speed %d of writer not sufficient for this medium.\n",
+ speed);
+ errmsgno(EX_BAD,
+ "You did use a %s speed medium on an improper writer or\n",
+ dp->cdr_dstat->ds_flags & DSF_ULTRASP_ERA ?
+ "ultra high": "high");
+ comerrno(EX_BAD,
+ "you used a speed=# option with a speed too low for this medium.\n");
+ }
+ }
+ }
+ if ((flags & (F_BLANK|F_FORCE)) == (F_BLANK|F_FORCE)) {
+ printf("Waiting for drive to calm down.\n");
+ wait_unit_ready(usalp, 120);
+ if (gracewait(dp, &gracedone) < 0) {
+ /*
+ * In case kill() did not work ;-)
+ */
+ errs++;
+ goto restore_it;
+ }
+ scsi_blank(usalp, 0L, blanktype, FALSE);
+ }
+
+ /*
+ * Last chance to quit!
+ */
+ if (gracewait(dp, &gracedone) < 0) {
+ /*
+ * In case kill() did not work ;-)
+ */
+ errs++;
+ goto restore_it;
+ }
+
+ if (dp->profile == 0x2B && flags & F_SAO && tsize > 0) {
+ printf("Preparing middle zone location for this DVD+R dual layer disc\n");
+ if (!dp->cdr_layer_split(usalp, dp, tsize)) {
+ errmsgno(EX_BAD, "Cannot send structure for middle zone location.\n");
+ comexit(EX_BAD);
+ }
+ }
+
+ if (tracks > 0 && fs > 0l) {
+ /*
+ * Wait for the read-buffer to become full.
+ * This should be take no extra time if the input is a file.
+ * If the input is a pipe (e.g. mkisofs) this can take a
+ * while. If mkisofs dumps core before it starts writing,
+ * we abort before the writing process started.
+ */
+ if (!await_faio()) {
+ comerrno(EX_BAD, "Input buffer error, aborting.\n");
+ }
+ }
+ wait_unit_ready(usalp, 120);
+
+ starttime.tv_sec = 0;
+ wstarttime.tv_sec = 0;
+ stoptime.tv_sec = 0;
+ fixtime.tv_sec = 0;
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+
+ /*
+ * Blank the media if we were requested to do so
+ */
+ if (flags & F_BLANK) {
+ /*
+ * Do not abort if OPC failes. Just give it a chance
+ * for better laser power calibration than without OPC.
+ *
+ * Ricoh drives return with a vendor unique sense code.
+ * This is most likely because they refuse to do OPC
+ * on a non blank media.
+ */
+ usalp->silent++;
+ do_opc(usalp, dp, flags);
+ usalp->silent--;
+ wait_unit_ready(usalp, 120);
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+
+ if ((*dp->cdr_blank)(usalp, dp, 0L, blanktype) < 0) {
+ errmsgno(EX_BAD, "Cannot blank disk, aborting.\n");
+ if (blanktype != BLANK_DISC) {
+ errmsgno(EX_BAD, "Some drives do not support all blank types.\n");
+ errmsgno(EX_BAD, "Try again with wodim blank=all.\n");
+ }
+ comexit(EX_BAD);
+ }
+ if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
+ errmsg("Cannot get blank time\n");
+ if (lverbose)
+ prtimediff("Blanking time: ", &starttime, &fixtime);
+
+ /*
+ * XXX Erst blank und dann format?
+ * XXX Wenn ja, dann hier (flags & F_FORMAT) testen
+ *
+ * EB: nee, besser nicht
+ */
+ if (!wait_unit_ready(usalp, 240) || tracks == 0) {
+ comexit(0);
+ }
+ }
+ if (flags & F_FORMAT) {
+ printf("wodim: media format asked\n");
+ /*
+ * Do not abort if OPC failes. Just give it a chance
+ * for better laser power calibration than without OPC.
+ *
+ * Ricoh drives return with a vendor unique sense code.
+ * This is most likely because they refuse to do OPC
+ * on a non blank media.
+ */
+ usalp->silent++;
+ do_opc(usalp, dp, flags);
+ usalp->silent--;
+ wait_unit_ready(usalp, 120);
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+
+ if ((*dp->cdr_format)(usalp, dp, formattype) < 0) {
+ errmsgno(EX_BAD, "Cannot format disk, aborting.\n");
+ comexit(EX_BAD);
+ }
+ if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
+ errmsg("Cannot get format time\n");
+ if (lverbose)
+ prtimediff("Formatting time: ", &starttime, &fixtime);
+
+ if (!wait_unit_ready(usalp, 240) || tracks == 0) {
+ comexit(0);
+ }
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+ }
+ /*
+ * Reset start time so we will not see blanking time and
+ * writing time counted together.
+ */
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+ if (tracks == 0 && (flags & F_FIX) == 0)
+ comerrno(EX_BAD, "No tracks found.\n");
+ /*
+ * Get the number of the next recordable track by reading the TOC and
+ * use the number the last current track number.
+ */
+ usalp->silent++;
+ if (read_tochdr(usalp, dp, NULL, &trackno) < 0) {
+ trackno = 0;
+ }
+ usalp->silent--;
+
+ /* If it is DVD, the information in TOC is fabricated :)
+ The real information is from read disk info command*/
+ if((dp->cdr_dstat->ds_disktype&DT_DVD) && (dp->cdr_dstat->ds_trlast>0)){
+ trackno=dp->cdr_dstat->ds_trlast-1;
+ if (lverbose > 2)
+ printf("trackno=%d\n",trackno);
+ }
+
+ if ((tracks + trackno) > MAX_TRACK) {
+ /*
+ * XXX How many tracks are allowed on a DVD?
+ */
+ comerrno(EX_BAD, "Too many tracks for this disk, last track number is %d.\n",
+ tracks + trackno);
+ }
+
+ for (i = 0; i <= tracks+1; i++) { /* Lead-in ... Lead-out */
+ track[i].trackno = i + trackno; /* Set up real track # */
+ }
+
+ if ((*dp->cdr_opt2)(usalp, dp) < 0) {
+ errmsgno(EX_BAD, "Cannot set up 2nd set of driver options.\n");
+ }
+
+ /*
+ * Now we actually start writing to the CD/DVD.
+ * XXX Check total size of the tracks and remaining size of disk.
+ */
+ if ((*dp->cdr_open_session)(usalp, dp, track) < 0) {
+ comerrno(EX_BAD, "Cannot open new session.\n");
+ }
+ if (!do_opc(usalp, dp, flags))
+ comexit(EX_BAD);
+
+ /*
+ * As long as open_session() will do nothing but
+ * set up parameters, we may leave fix_it here.
+ * I case we have to add an open_session() for a drive
+ * that wants to do something that modifies the disk
+ * We have to think about a new solution.
+ */
+ if (flags & F_FIX)
+ goto fix_it;
+
+ /*
+ * This call may modify trackp[i].trackstart for all tracks.
+ */
+ if ((*dp->cdr_write_leadin)(usalp, dp, track) < 0)
+ comerrno(EX_BAD, "Could not write Lead-in.\n");
+
+ if (lverbose && (dp->cdr_dstat->ds_cdrflags & RF_LEADIN) != 0) {
+
+ if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
+ errmsg("Cannot get lead-in write time\n");
+ prtimediff("Lead-in write time: ", &starttime, &fixtime);
+ }
+
+ if (gettimeofday(&wstarttime, (struct timezone *)0) < 0)
+ errmsg("Cannot get start time\n");
+ for (i = 1; i <= tracks; i++) {
+ startsec = 0L;
+
+ if ((*dp->cdr_open_track)(usalp, dp, &track[i]) < 0) {
+ errmsgno(EX_BAD, "Cannot open next track.\n");
+ errs++;
+ break;
+ }
+
+ if ((flags & (F_SAO|F_RAW)) == 0) {
+ if ((*dp->cdr_next_wr_address)(usalp, &track[i], &startsec) < 0) {
+ errmsgno(EX_BAD, "Cannot get next writable address.\n");
+ errs++;
+ break;
+ }
+ track[i].trackstart = startsec;
+ }
+ if (debug || lverbose) {
+ printf("Starting new track at sector: %ld\n",
+ track[i].trackstart);
+ flush();
+ }
+ if (write_track_data(usalp, dp, &track[i]) < 0) {
+ if (cdr_underrun(usalp)) {
+ errmsgno(EX_BAD,
+ "The current problem looks like a buffer underrun.\n");
+ if ((dp->cdr_dstat->ds_cdrflags & RF_BURNFREE) == 0)
+ errmsgno(EX_BAD, "Try to use 'driveropts=burnfree'.\n");
+ else {
+ errmsgno(EX_BAD, "It looks like 'driveropts=burnfree' does not work for this drive.\n");
+ errmsgno(EX_BAD, "Please report.\n");
+ }
+
+ errmsgno(EX_BAD,
+ "Make sure that you are root, enable DMA and check your HW/OS set up.\n");
+ } else {
+ errmsgno(EX_BAD, "A write error occured.\n");
+ errmsgno(EX_BAD, "Please properly read the error message above.\n");
+ }
+ errs++;
+ sleep(5);
+ unit_ready(usalp);
+ (*dp->cdr_close_track)(usalp, dp, &track[i]);
+ break;
+ }
+ if ((*dp->cdr_close_track)(usalp, dp, &track[i]) < 0) {
+ /*
+ * Check for "Dummy blocks added" message first.
+ */
+ if (usal_sense_key(usalp) != SC_ILLEGAL_REQUEST ||
+ usal_sense_code(usalp) != 0xB5) {
+ errmsgno(EX_BAD, "Cannot close track.\n");
+ errs++;
+ break;
+ }
+ }
+ }
+fix_it:
+ if (gettimeofday(&stoptime, (struct timezone *)0) < 0)
+ errmsg("Cannot get stop time\n");
+ cdrstats(dp);
+
+ if (flags & F_RAW) {
+ if (lverbose) {
+ printf("Writing Leadout...\n");
+ flush();
+ }
+ write_leadout(usalp, dp, track);
+ }
+ if ((flags & F_NOFIX) == 0) {
+ if (lverbose) {
+ printf("Fixating...\n");
+ flush();
+ }
+ if ((*dp->cdr_fixate)(usalp, dp, track) < 0) {
+ /*
+ * Ignore fixating errors in dummy mode.
+ */
+ if ((flags & F_DUMMY) == 0) {
+ errmsgno(EX_BAD, "Cannot fixate disk.\n");
+ errs++;
+ }
+ }
+ if (gettimeofday(&fixtime, (struct timezone *)0) < 0)
+ errmsg("Cannot get fix time\n");
+ if (lverbose)
+ prtimediff("Fixating time: ", &stoptime, &fixtime);
+ }
+ if ((dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) {
+ dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT;
+ (*dp->cdr_stats)(usalp, dp);
+ }
+ if ((flags & (F_RAW|F_EJECT)) == F_RAW) {
+ /*
+ * Most drives seem to forget to reread the TOC from disk
+ * if they are in RAW mode.
+ */
+ usalp->silent++;
+ if (read_tochdr(usalp, dp, NULL, NULL) < 0) {
+ usalp->silent--;
+ if ((flags & F_DUMMY) == 0)
+ reload_media(usalp, dp);
+ } else {
+ usalp->silent--;
+ }
+ }
+
+restore_it:
+ /*
+ * Try to restore the old sector size and stop FIFO.
+ */
+ kill_faio();
+ comexit(errs?-2:0);
+ return (0);
+}
+
+static int
+gracewait(cdr_t *dp, BOOL *didgracep)
+{
+ int i;
+ BOOL didgrace = FALSE;
+
+ if (didgracep)
+ didgrace = *didgracep;
+
+ if(warn_minisize>=0) {
+ fprintf(stderr, "\nWARNING: found a microscopic small track size (%lld bytes).\n"
+ " Do you really want to write this image? Press Ctrl-C to abort...\n\n",
+ warn_minisize);
+ gracetime+=20;
+ }
+ if (gracetime < MIN_GRACE_TIME)
+ gracetime = MIN_GRACE_TIME;
+ if (gracetime > 999)
+ gracetime = 999;
+
+ printf("Starting to write CD/DVD at speed %5.1f in %s%s %s mode for %s session.\n",
+ (float)dp->cdr_dstat->ds_wspeed,
+ (dp->cdr_cmdflags & F_DUMMY) ? "dummy" : "real",
+ (dp->cdr_cmdflags & F_FORCE) ? " force" : "",
+ wm2name[dp->cdr_dstat->ds_wrmode],
+ (dp->cdr_cmdflags & F_MULTI) ? "multi" : "single");
+ if (didgrace) {
+ printf("No chance to quit anymore.");
+ goto grace_done;
+ }
+ printf("Last chance to quit, starting %s write in %4d seconds.",
+ (dp->cdr_cmdflags & F_DUMMY)?"dummy":"real", gracetime);
+ flush();
+ signal(SIGINT, intr);
+ signal(SIGHUP, intr);
+ signal(SIGTERM, intr);
+
+ for (i = gracetime; --i >= 0; ) {
+ sleep(1);
+ if (didintr) {
+ printf("\n");
+ excdr(SIGINT, &exargs);
+ signal(SIGINT, SIG_DFL);
+ kill(getpid(), SIGINT);
+ /*
+ * In case kill() did not work ;-)
+ */
+ if (didgracep)
+ *didgracep = FALSE;
+ return (-1);
+ }
+ printf("\b\b\b\b\b\b\b\b\b\b\b\b\b%4d seconds.", i);
+ flush();
+ }
+grace_done:
+ printf(" Operation starts.");
+ flush();
+ signal(SIGINT, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, intfifo);
+ signal(SIGHUP, intfifo);
+ signal(SIGTERM, intfifo);
+ printf("\n");
+
+ if (didgracep)
+ *didgracep = TRUE;
+ return (0);
+}
+
+static void
+cdrstats(cdr_t *dp)
+{
+ float secsps = 75.0;
+ int nsecs;
+ float fspeed;
+ struct timeval tcur;
+ struct timeval tlast;
+ BOOL nostop = FALSE;
+
+ if (starttime.tv_sec == 0)
+ return;
+
+ if (stoptime.tv_sec == 0) {
+ gettimeofday(&stoptime, (struct timezone *)0);
+ nostop = TRUE;
+ }
+
+ if ((dp->cdr_dstat->ds_cdrflags & RF_DID_STAT) != 0)
+ return;
+ dp->cdr_dstat->ds_cdrflags |= RF_DID_STAT;
+
+ if (lverbose == 0)
+ return;
+
+ if (dp->cdr_cmdflags & F_FIX)
+ return;
+
+ if ((dp->cdr_cmdflags & (F_WRITE|F_BLANK)) == F_BLANK)
+ return;
+
+ tlast = wstarttime;
+ tcur = stoptime;
+
+ prtimediff("Writing time: ", &starttime, &stoptime);
+
+ nsecs = dp->cdr_dstat->ds_endsec - dp->cdr_dstat->ds_startsec;
+
+ if (dp->cdr_dstat->ds_flags & DSF_DVD)
+ secsps = 676.27;
+
+ tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
+ tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
+ while (tlast.tv_usec < 0) {
+ tlast.tv_usec += 1000000;
+ tlast.tv_sec -= 1;
+ }
+ if (!nostop && nsecs != 0 && dp->cdr_dstat->ds_endsec > 0) {
+ /*
+ * May not be known (e.g. cdrecord -)
+ *
+ * XXX if we later allow this code to know how much has
+ * XXX actually been written, then we may remove the
+ * XXX dependance from nostop & nsecs != 0
+ */
+ fspeed = (nsecs / secsps) /
+ (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
+ if (fspeed > 999.0)
+ fspeed = 999.0;
+ if (dp->is_dvd) fspeed /= 9;
+ printf("Average write speed %5.1fx.\n", fspeed);
+ }
+
+ if (dp->cdr_dstat->ds_minbuf <= 100) {
+ printf("Min drive buffer fill was %u%%\n",
+ (unsigned int)dp->cdr_dstat->ds_minbuf);
+ }
+ if (dp->cdr_dstat->ds_buflow > 0) {
+ printf("Total of %ld possible drive buffer underruns predicted.\n",
+ (long)dp->cdr_dstat->ds_buflow);
+ }
+}
+
+/*
+ * Short usage
+ */
+static void
+susage(int ret)
+{
+ fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname());
+ fprintf(stderr, "\nUse\t%s -help\n", get_progname());
+ fprintf(stderr, "to get a list of valid options.\n");
+ fprintf(stderr, "\nUse\t%s blank=help\n", get_progname());
+ fprintf(stderr, "to get a list of valid blanking options.\n");
+ fprintf(stderr, "\nUse\t%s dev=b,t,l driveropts=help -checkdrive\n", get_progname());
+ fprintf(stderr, "to get a list of drive specific options.\n");
+ fprintf(stderr, "\nUse\t%s dev=help\n", get_progname());
+ fprintf(stderr, "to get a list of possible SCSI transport specifiers.\n");
+ exit(ret);
+ /* NOTREACHED */
+}
+
+static void
+usage(int excode)
+{
+ fprintf(stderr, "Usage: %s [options] track1...trackn\n", get_progname());
+ fprintf(stderr, "Options:\n");
+ fprintf(stderr, "\t-version print version information and exit\n");
+ fprintf(stderr, "\tdev=target SCSI target to use as CD/DVD-Recorder\n");
+ fprintf(stderr, "\tgracetime=# set the grace time before starting to write to #.\n");
+ fprintf(stderr, "\ttimeout=# set the default SCSI command timeout to #.\n");
+ fprintf(stderr, "\tdebug=#,-d Set to # or increment misc debug level\n");
+ fprintf(stderr, "\tkdebug=#,kd=# do Kernel debugging\n");
+ fprintf(stderr, "\t-verbose,-v increment general verbose level by one\n");
+ fprintf(stderr, "\t-Verbose,-V increment SCSI command transport verbose level by one\n");
+ fprintf(stderr, "\t-silent,-s do not print status of failed SCSI commands\n");
+ fprintf(stderr, "\tdriver=name user supplied driver name, use with extreme care\n");
+ fprintf(stderr, "\tdriveropts=opt a comma separated list of driver specific options\n");
+ fprintf(stderr, "\t-setdropts set driver specific options and exit\n");
+ fprintf(stderr, "\t-checkdrive check if a driver for the drive is present\n");
+ fprintf(stderr, "\t-prcap print drive capabilities for MMC compliant drives\n");
+ fprintf(stderr, "\t-inq do an inquiry for the drive and exit\n");
+ fprintf(stderr, "\t-scanbus scan the SCSI and IDE buses and exit\n");
+ fprintf(stderr, "\t-reset reset the SCSI bus with the cdrecorder (if possible)\n");
+ fprintf(stderr, "\t-abort send an abort sequence to the drive (may help if hung)\n");
+ fprintf(stderr, "\t-overburn allow to write more than the official size of a medium\n");
+ fprintf(stderr, "\t-ignsize ignore the known size of a medium (may cause problems)\n");
+ fprintf(stderr, "\t-useinfo use *.inf files to overwrite audio options.\n");
+ fprintf(stderr, "\tspeed=# set speed of drive\n");
+ fprintf(stderr, "\tblank=type blank a CD-RW disc (see blank=help)\n");
+ fprintf(stderr, "\t-format format a CD-RW/DVD-RW/DVD+RW disc\n");
+ fprintf(stderr, "\tformattype=# select the format method for DVD+RW disc\n");
+#ifdef FIFO
+ fprintf(stderr, "\tfs=# Set fifo size to # (0 to disable, default is %ld MB)\n",
+ DEFAULT_FIFOSIZE/(1024L*1024L));
+#endif
+ fprintf(stderr, "\tts=# set maximum transfer size for a single SCSI command\n");
+ fprintf(stderr, "\t-load load the disk and exit (works only with tray loader)\n");
+ fprintf(stderr, "\t-lock load and lock the disk and exit (works only with tray loader)\n");
+ fprintf(stderr, "\t-eject eject the disk after doing the work\n");
+ fprintf(stderr, "\t-dummy do everything with laser turned off\n");
+ fprintf(stderr, "\t-msinfo retrieve multi-session info for genisoimage\n");
+ fprintf(stderr, "\t-msifile=path run -msinfo and copy output to file\n");
+ fprintf(stderr, "\t-toc retrieve and print TOC/PMA data\n");
+ fprintf(stderr, "\t-atip retrieve and print ATIP data\n");
+ fprintf(stderr, "\t-multi generate a TOC that allows multi session\n");
+ fprintf(stderr, "\t In this case default track type is CD-ROM XA mode 2 form 1 - 2048 bytes\n");
+ fprintf(stderr, "\t-fix fixate a corrupt or unfixated disk (generate a TOC)\n");
+ fprintf(stderr, "\t-nofix do not fixate disk after writing tracks\n");
+ fprintf(stderr, "\t-waiti wait until input is available before opening SCSI\n");
+ fprintf(stderr, "\t-immed Try to use the SCSI IMMED flag with certain long lasting commands\n");
+ fprintf(stderr, "\t-force force to continue on some errors to allow blanking bad disks\n");
+ fprintf(stderr, "\t-tao Write disk in TAO mode.\n");
+ fprintf(stderr, "\t-dao Write disk in SAO mode.\n");
+ fprintf(stderr, "\t-sao Write disk in SAO mode.\n");
+ fprintf(stderr, "\t-raw Write disk in RAW mode.\n");
+ fprintf(stderr, "\t-raw96r Write disk in RAW/RAW96R mode.\n");
+ fprintf(stderr, "\t-raw96p Write disk in RAW/RAW96P mode.\n");
+ fprintf(stderr, "\t-raw16 Write disk in RAW/RAW16 mode.\n");
+#ifdef CLONE_WRITE
+ fprintf(stderr, "\t-clone Write disk in clone write mode.\n");
+#endif
+ fprintf(stderr, "\ttsize=# Length of valid data in next track\n");
+ fprintf(stderr, "\tpadsize=# Amount of padding for next track\n");
+ fprintf(stderr, "\tpregap=# Amount of pre-gap sectors before next track\n");
+ fprintf(stderr, "\tdefpregap=# Amount of pre-gap sectors for all but track #1\n");
+ fprintf(stderr, "\tmcn=text Set the media catalog number for this CD to 'text'\n");
+ fprintf(stderr, "\tisrc=text Set the ISRC number for the next track to 'text'\n");
+ fprintf(stderr, "\tindex=list Set the index list for the next track to 'list'\n");
+ fprintf(stderr, "\t-text Write CD-Text from information from *.inf or *.cue files\n");
+ fprintf(stderr, "\ttextfile=name Set the file with CD-Text data to 'name'\n");
+ fprintf(stderr, "\tcuefile=name Set the file with CDRWIN CUE data to 'name'\n");
+
+ fprintf(stderr, "\t-audio Subsequent tracks are CD-DA audio tracks\n");
+ fprintf(stderr, "\t-data Subsequent tracks are CD-ROM data mode 1 - 2048 bytes (default)\n");
+ fprintf(stderr, "\t-mode2 Subsequent tracks are CD-ROM data mode 2 - 2336 bytes\n");
+ fprintf(stderr, "\t-xa Subsequent tracks are CD-ROM XA mode 2 form 1 - 2048 bytes\n");
+ fprintf(stderr, "\t-xa1 Subsequent tracks are CD-ROM XA mode 2 form 1 - 2056 bytes\n");
+ fprintf(stderr, "\t-xa2 Subsequent tracks are CD-ROM XA mode 2 form 2 - 2324 bytes\n");
+ fprintf(stderr, "\t-xamix Subsequent tracks are CD-ROM XA mode 2 form 1/2 - 2332 bytes\n");
+ fprintf(stderr, "\t-cdi Subsequent tracks are CDI tracks\n");
+ fprintf(stderr, "\t-isosize Use iso9660 file system size for next data track\n");
+ fprintf(stderr, "\t-preemp Audio tracks are mastered with 50/15 microseconds preemphasis\n");
+ fprintf(stderr, "\t-nopreemp Audio tracks are mastered with no preemphasis (default)\n");
+ fprintf(stderr, "\t-copy Audio tracks have unlimited copy permission\n");
+ fprintf(stderr, "\t-nocopy Audio tracks may only be copied once for personal use (default)\n");
+ fprintf(stderr, "\t-scms Audio tracks will not have any copy permission at all\n");
+ fprintf(stderr, "\t-pad Pad data tracks with %d zeroed sectors\n", PAD_SECS);
+ fprintf(stderr, "\t Pad audio tracks to a multiple of %d bytes\n", AUDIO_SEC_SIZE);
+ fprintf(stderr, "\t-nopad Do not pad data tracks (default)\n");
+ fprintf(stderr, "\t-shorttrack Subsequent tracks may be non Red Book < 4 seconds if in SAO or RAW mode\n");
+ fprintf(stderr, "\t-noshorttrack Subsequent tracks must be >= 4 seconds\n");
+ fprintf(stderr, "\t-swab Audio data source is byte-swapped (little-endian/Intel)\n");
+ fprintf(stderr, "The type of the first track is used for the toc type.\n");
+ fprintf(stderr, "Currently only form 1 tracks are supported.\n");
+ exit(excode);
+}
+
+static void
+blusage(int ret)
+{
+ fprintf(stderr, "Blanking options:\n");
+ fprintf(stderr, "\tall\t\tblank the entire disk\n");
+ fprintf(stderr, "\tdisc\t\tblank the entire disk\n");
+ fprintf(stderr, "\tdisk\t\tblank the entire disk\n");
+ fprintf(stderr, "\tfast\t\tminimally blank the entire disk (PMA, TOC, pregap)\n");
+ fprintf(stderr, "\tminimal\t\tminimally blank the entire disk (PMA, TOC, pregap)\n");
+ fprintf(stderr, "\ttrack\t\tblank a track\n");
+ fprintf(stderr, "\tunreserve\tunreserve a track\n");
+ fprintf(stderr, "\ttrtail\t\tblank a track tail\n");
+ fprintf(stderr, "\tunclose\t\tunclose last session\n");
+ fprintf(stderr, "\tsession\t\tblank last session\n");
+
+ exit(ret);
+ /* NOTREACHED */
+}
+
+static void
+formattypeusage(int ret)
+{
+ fprintf(stderr, "Formating options:\n");
+ fprintf(stderr, "\tfull\t\tstandard formating\n");
+ fprintf(stderr, "\tbackground\t\tbackground formating\n");
+ fprintf(stderr, "\tforce\t\tforce reformat\n");
+
+ exit(ret);
+ /* NOTREACHED */
+}
+
+/* ARGSUSED */
+static void
+intr(int sig)
+{
+ sig = 0; /* Fake usage for gcc */
+
+ signal(SIGINT, intr);
+
+ didintr++;
+}
+
+static void
+catchsig(int sig)
+{
+ signal(sig, catchsig);
+}
+
+static int
+scsi_cb(void *arg)
+{
+ comexit(EX_BAD);
+ /* NOTREACHED */
+ return (0); /* Keep lint happy */
+}
+
+static void
+intfifo(int sig)
+{
+ errmsgno(EX_BAD, "Caught interrupt.\n");
+ if (exargs.usalp) {
+ SCSI *usalp = exargs.usalp;
+
+ if (usalp->running) {
+ if (usalp->cb_fun != NULL) {
+ comerrno(EX_BAD, "Second interrupt. Doing hard abort.\n");
+ /* NOTREACHED */
+ }
+ usalp->cb_fun = scsi_cb;
+ usalp->cb_arg = &exargs;
+ return;
+ }
+ }
+ comexit(sig);
+}
+
+/* ARGSUSED */
+static void
+exscsi(int excode, void *arg)
+{
+ struct exargs *exp = (struct exargs *)arg;
+
+ /*
+ * Try to restore the old sector size.
+ */
+ if (exp != NULL && exp->exflags == 0) {
+ if (exp->usalp->running) {
+ return;
+ }
+ /*
+ * flush cache is not supported by CD-ROMs avoid prob with -toc
+ */
+ exp->usalp->silent++;
+ scsi_flush_cache(exp->usalp, FALSE);
+ (*exp->dp->cdr_abort_session)(exp->usalp, exp->dp);
+ exp->usalp->silent--;
+ set_secsize(exp->usalp, exp->old_secsize);
+ unload_media(exp->usalp, exp->dp, exp->flags);
+
+ exp->exflags++; /* Make sure that it only get called once */
+ }
+}
+
+static void
+excdr(int excode, void *arg)
+{
+ struct exargs *exp = (struct exargs *)arg;
+
+ exscsi(excode, arg);
+
+ cdrstats(exp->dp);
+ if ((exp->dp->cdr_dstat->ds_cdrflags & RF_DID_CDRSTAT) == 0) {
+ exp->dp->cdr_dstat->ds_cdrflags |= RF_DID_CDRSTAT;
+ (*exp->dp->cdr_stats)(exp->usalp, exp->dp);
+ }
+
+#ifdef FIFO
+ kill_faio();
+ wait_faio();
+ if (debug || lverbose)
+ fifo_stats();
+#endif
+}
+
+int
+read_buf(int f, char *bp, int size)
+{
+ char *p = bp;
+ int amount = 0;
+ int n;
+
+ do {
+ do {
+ n = read(f, p, size-amount);
+ } while (n < 0 && (geterrno() == EAGAIN || geterrno() == EINTR));
+ if (n < 0)
+ return (n);
+ amount += n;
+ p += n;
+
+ } while (amount < size && n > 0);
+ return (amount);
+}
+
+int
+fill_buf(int f, track_t *trackp, long secno, char *bp, int size)
+{
+ int amount = 0;
+ int nsecs;
+ int rsize;
+ int rmod;
+ int readoffset = 0;
+
+ nsecs = size / trackp->secsize;
+ if (nsecs < trackp->secspt) {
+ /*
+ * Clear buffer to prepare for last transfer.
+ * Make sure that a partial sector ends with NULs
+ */
+ fillbytes(bp, trackp->secspt * trackp->secsize, '\0');
+ }
+
+ if (!is_raw(trackp)) {
+ amount = read_buf(f, bp, size);
+ if (amount != size) {
+ if (amount < 0)
+ return (amount);
+ /*
+ * We got less than expected, clear rest of buf.
+ */
+ fillbytes(&bp[amount], size-amount, '\0');
+ }
+ if (is_swab(trackp))
+ swabbytes(bp, amount);
+ return (amount);
+ }
+
+ rsize = nsecs * trackp->isecsize;
+ rmod = size % trackp->secsize;
+ if (rmod > 0) {
+ rsize += rmod;
+ nsecs++;
+ }
+
+ readoffset = trackp->dataoff;
+ amount = read_buf(f, bp + readoffset, rsize);
+ if (is_swab(trackp))
+ swabbytes(bp + readoffset, amount);
+
+ if (trackp->isecsize == 2448 && trackp->secsize == 2368)
+ subrecodesecs(trackp, (Uchar *)bp, secno, nsecs);
+
+ scatter_secs(trackp, bp + readoffset, nsecs);
+
+ if (amount != rsize) {
+ if (amount < 0)
+ return (amount);
+ /*
+ * We got less than expected, clear rest of buf.
+ */
+ fillbytes(&bp[amount], rsize-amount, '\0');
+ nsecs = amount / trackp->isecsize;
+ rmod = amount % trackp->isecsize;
+ amount = nsecs * trackp->secsize;
+ if (rmod > 0) {
+ nsecs++;
+ amount += rmod;
+ }
+ } else {
+ amount = size;
+ }
+ if ((trackp->sectype & ST_MODE_RAW) == 0) {
+ encsectors(trackp, (Uchar *)bp, secno, nsecs);
+ fillsubch(trackp, (Uchar *)bp, secno, nsecs);
+ } else {
+ scrsectors(trackp, (Uchar *)bp, secno, nsecs);
+ }
+ return (amount);
+}
+
+int
+get_buf(int f, track_t *trackp, long secno, char **bpp, int size)
+{
+ if (fs > 0) {
+/* return (faio_read_buf(f, *bpp, size));*/
+ return (faio_get_buf(f, bpp, size));
+ } else {
+ return (fill_buf(f, trackp, secno, *bpp, size));
+ }
+}
+
+int
+write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec, int bytespt,
+ int secspt, BOOL islast)
+{
+ int amount;
+
+again:
+ usalp->silent++;
+ amount = (*dp->cdr_write_trackdata)(usalp, bp, startsec, bytespt, secspt, islast);
+ usalp->silent--;
+ if (amount < 0) {
+ if (scsi_in_progress(usalp)) {
+ /*
+ * If we sleep too long, the drive buffer is empty
+ * before we start filling it again. The max. CD speed
+ * is ~ 10 MB/s (52x RAW writing). The max. DVD speed
+ * is ~ 25 MB/s (18x DVD 1385 kB/s).
+ * With 10 MB/s, a 1 MB buffer empties within 100ms.
+ * With 25 MB/s, a 1 MB buffer empties within 40ms.
+ */
+ if ((dp->cdr_dstat->ds_flags & DSF_DVD) == 0) {
+ usleep(60000);
+ } else {
+#ifndef _SC_CLK_TCK
+ usleep(20000);
+#else
+ if (sysconf(_SC_CLK_TCK) < 100)
+ usleep(20000);
+ else
+ usleep(10000);
+
+#endif
+ }
+ goto again;
+ }
+ return (-1);
+ }
+ return (amount);
+}
+
+static int
+write_track_data(SCSI *usalp, cdr_t *dp, track_t *trackp)
+{
+ int track = trackp->trackno;
+ int f = -1;
+ int isaudio;
+ long startsec;
+ Llong bytes_read = 0;
+ Llong bytes = 0;
+ Llong savbytes = 0;
+ int count;
+ Llong tracksize;
+ int secsize;
+ int secspt;
+ int bytespt;
+ int bytes_to_read;
+ long amount;
+ int pad;
+ BOOL neednl = FALSE;
+ BOOL islast = FALSE;
+ char *bp = buf;
+ struct timeval tlast;
+ struct timeval tcur;
+ float secsps = 75.0;
+long bsize;
+long bfree;
+#define BCAP
+#ifdef BCAP
+int per = 0;
+#ifdef XBCAP
+int oper = -1;
+#endif
+#endif
+
+ if (dp->cdr_dstat->ds_flags & DSF_DVD)
+ secsps = 676.27;
+
+ usalp->silent++;
+ if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0)
+ bsize = -1L;
+ if (bsize == 0) /* If we have no (known) buffer, we cannot */
+ bsize = -1L; /* retrieve the buffer fill ratio */
+ usalp->silent--;
+
+
+ if (is_packet(trackp)) /* XXX Ugly hack for now */
+ return (write_packet_data(usalp, dp, trackp));
+
+ if (trackp->xfp != NULL)
+ f = xfileno(trackp->xfp);
+
+ isaudio = is_audio(trackp);
+ tracksize = trackp->tracksize;
+ startsec = trackp->trackstart;
+
+ secsize = trackp->secsize;
+ secspt = trackp->secspt;
+ bytespt = secsize * secspt;
+
+ pad = !isaudio && is_pad(trackp); /* Pad only data tracks */
+
+ if (debug) {
+ printf("secsize:%d secspt:%d bytespt:%d audio:%d pad:%d\n",
+ secsize, secspt, bytespt, isaudio, pad);
+ }
+
+ if (lverbose) {
+ if (tracksize > 0)
+ printf("\rTrack %02d: 0 of %4lld MB written.",
+ track, tracksize >> 20);
+ else
+ printf("\rTrack %02d: 0 MB written.", track);
+ flush();
+ neednl = TRUE;
+ }
+
+ gettimeofday(&tlast, (struct timezone *)0);
+ do {
+ bytes_to_read = bytespt;
+ if (tracksize > 0) {
+ if ((tracksize - bytes_read) > bytespt)
+ bytes_to_read = bytespt;
+ else
+ bytes_to_read = tracksize - bytes_read;
+ }
+ count = get_buf(f, trackp, startsec, &bp, bytes_to_read);
+
+ if (count < 0)
+ comerr("read error on input file\n");
+ if (count == 0)
+ break;
+ bytes_read += count;
+ if (tracksize >= 0 && bytes_read >= tracksize) {
+ count -= bytes_read - tracksize;
+ /*
+ * Paranoia: tracksize is known (trackp->tracksize >= 0)
+ * At this point, trackp->padsize should alway be set
+ * if the tracksize is less than 300 sectors.
+ */
+ if (trackp->padsecs == 0 &&
+ (is_shorttrk(trackp) || (bytes_read/secsize) >= 300))
+ islast = TRUE;
+ }
+
+ if (count < bytespt) {
+ if (debug) {
+ printf("\nNOTICE: reducing block size for last record.\n");
+ neednl = FALSE;
+ }
+
+ if ((amount = count % secsize) != 0) {
+ amount = secsize - amount;
+ count += amount;
+ printf("\nWARNING: padding up to secsize.\n");
+ neednl = FALSE;
+ }
+ bytespt = count;
+ secspt = count / secsize;
+ /*
+ * If tracksize is not known (trackp->tracksize < 0)
+ * we may need to set trackp->padsize
+ * if the tracksize is less than 300 sectors.
+ */
+ if (trackp->padsecs == 0 &&
+ (is_shorttrk(trackp) || (bytes_read/secsize) >= 300))
+ islast = TRUE;
+ }
+
+ amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast);
+ if (amount < 0) {
+ printf("%swrite track data: error after %lld bytes\n",
+ neednl?"\n":"", bytes);
+ return (-1);
+ }
+ bytes += amount;
+ startsec += amount / secsize;
+
+ if (lverbose && (bytes >= (savbytes + 0x100000))) {
+ int fper;
+ int nsecs = (bytes - savbytes) / secsize;
+ float fspeed;
+
+ gettimeofday(&tcur, (struct timezone *)0);
+ printf("\rTrack %02d: %4lld", track, bytes >> 20);
+ if (tracksize > 0)
+ printf(" of %4lld MB", tracksize >> 20);
+ else
+ printf(" MB");
+ printf(" written");
+ fper = fifo_percent(TRUE);
+ if (fper >= 0)
+ printf(" (fifo %3d%%)", fper);
+#ifdef BCAP
+ if (bsize > 0) { /* buffer size known */
+ usalp->silent++;
+ per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
+ usalp->silent--;
+ if (per >= 0) {
+ per = 100*(bsize - bfree) / bsize;
+ if ((bsize - bfree) <= amount || per <= 5)
+ dp->cdr_dstat->ds_buflow++;
+ if (per < (int)dp->cdr_dstat->ds_minbuf &&
+ (startsec*secsize) > bsize) {
+ dp->cdr_dstat->ds_minbuf = per;
+ }
+ printf(" [buf %3d%%]", per);
+#ifdef BCAPDBG
+ printf(" %3ld %3ld", bsize >> 10, bfree >> 10);
+#endif
+ }
+ }
+#endif
+
+ tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
+ tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
+ while (tlast.tv_usec < 0) {
+ tlast.tv_usec += 1000000;
+ tlast.tv_sec -= 1;
+ }
+ fspeed = (nsecs / secsps) /
+ (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
+ if (fspeed > 999.0)
+ fspeed = 999.0;
+#ifdef BCAP
+ if (bsize > 0 && per > dminbuf &&
+ dp->cdr_dstat->ds_cdrflags & RF_WR_WAIT) {
+ int wsecs = (per-dminbuf)*(bsize/secsize)/100;
+ int msecs = 0x100000/secsize;
+ int wt;
+ int mt;
+ int s = dp->cdr_dstat->ds_dr_cur_wspeed;
+
+
+ if (s <= 0) {
+ if (dp->cdr_dstat->ds_flags & DSF_DVD)
+ s = 4;
+ else
+ s = 50;
+ }
+ if (wsecs > msecs) /* Less that 1 MB */
+ wsecs = msecs;
+ wt = wsecs * 1000 / secsps / fspeed;
+ mt = (per-dminbuf)*(bsize/secsize)/100 * 1000 / secsps/s;
+
+ if (wt > mt)
+ wt = mt;
+ if (wt > 1000) /* Max 1 second */
+ wt = 1000;
+ if (wt < 20) /* Min 20 ms */
+ wt = 0;
+
+ if (xdebug)
+ printf(" |%3d %4dms %5dms|", wsecs, wt, mt);
+ else
+ printf(" |%3d %4dms|", wsecs, wt);
+ if (wt > 0)
+ usleep(wt*1000);
+ }
+#endif
+ if (dp->is_dvd) fspeed /= 9;
+ printf(" %5.1fx", fspeed);
+ printf(".");
+ savbytes = (bytes >> 20) << 20;
+ flush();
+ neednl = TRUE;
+ tlast = tcur;
+ }
+#ifdef XBCAP
+ if (bsize > 0) { /* buffer size known */
+ (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
+ per = 100*(bsize - bfree) / bsize;
+ if (per != oper)
+ printf("[buf %3d%%] %3ld %3ld\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b",
+ per, bsize >> 10, bfree >> 10);
+ oper = per;
+ flush();
+ }
+
+
+#endif
+ } while (tracksize < 0 || bytes_read < tracksize);
+
+ if (!is_shorttrk(trackp) && (bytes / secsize) < 300) {
+ /*
+ * If tracksize is not known (trackp->tracksize < 0) or
+ * for some strange reason we did not set padsecs properly
+ * we may need to modify trackp->padsecs if
+ * tracksize+padsecs is less than 300 sectors.
+ */
+ if ((trackp->padsecs + (bytes / secsize)) < 300)
+ trackp->padsecs = 300 - (bytes / secsize);
+ }
+ if (trackp->padsecs > 0) {
+ Llong padbytes;
+
+ /*
+ * pad_track() is based on secsize. Compute the amount of bytes
+ * assumed by pad_track().
+ */
+ padbytes = (Llong)trackp->padsecs * secsize;
+
+ if (neednl) {
+ printf("\n");
+ neednl = FALSE;
+ }
+ if ((padbytes >> 20) > 0) {
+ neednl = TRUE;
+ } else if (lverbose) {
+ printf("Track %02d: writing %3lld KB of pad data.\n",
+ track, (Llong)(padbytes >> 10));
+ neednl = FALSE;
+ }
+ pad_track(usalp, dp, trackp, startsec, padbytes,
+ TRUE, &savbytes);
+ bytes += savbytes;
+ startsec += savbytes / secsize;
+ }
+ printf("%sTrack %02d: Total bytes read/written: %lld/%lld (%lld sectors).\n",
+ neednl?"\n":"", track, bytes_read, bytes, bytes/secsize);
+ flush();
+ return (0);
+}
+
+int
+pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp, long startsec, Llong amt,
+ BOOL dolast, Llong *bytesp)
+{
+ int track = trackp->trackno;
+ Llong bytes = 0;
+ Llong savbytes = 0;
+ Llong padsize = amt;
+ int secsize;
+ int secspt;
+ int bytespt;
+ int amount;
+ BOOL neednl = FALSE;
+ BOOL islast = FALSE;
+ struct timeval tlast;
+ struct timeval tcur;
+ float secsps = 75.0;
+long bsize;
+long bfree;
+#define BCAP
+#ifdef BCAP
+int per;
+#ifdef XBCAP
+int oper = -1;
+#endif
+#endif
+
+ if (dp->cdr_dstat->ds_flags & DSF_DVD)
+ secsps = 676.27;
+
+ usalp->silent++;
+ if ((*dp->cdr_buffer_cap)(usalp, &bsize, &bfree) < 0)
+ bsize = -1L;
+ if (bsize == 0) /* If we have no (known) buffer, we cannot */
+ bsize = -1L; /* retrieve the buffer fill ratio */
+ usalp->silent--;
+
+ secsize = trackp->secsize;
+ secspt = trackp->secspt;
+ bytespt = secsize * secspt;
+
+ fillbytes(buf, bytespt, '\0');
+
+ if ((amt >> 20) > 0) {
+ printf("\rTrack %02d: 0 of %4lld MB pad written.",
+ track, amt >> 20);
+ flush();
+ }
+ gettimeofday(&tlast, (struct timezone *)0);
+ do {
+ if (amt < bytespt) {
+ bytespt = roundup(amt, secsize);
+ secspt = bytespt / secsize;
+ }
+ if (dolast && (amt - bytespt) <= 0)
+ islast = TRUE;
+
+ if (is_raw(trackp)) {
+ encsectors(trackp, (Uchar *)buf, startsec, secspt);
+ fillsubch(trackp, (Uchar *)buf, startsec, secspt);
+ }
+
+ amount = write_secs(usalp, dp, buf, startsec, bytespt, secspt, islast);
+ if (amount < 0) {
+ printf("%swrite track pad data: error after %lld bytes\n",
+ neednl?"\n":"", bytes);
+ if (bytesp)
+ *bytesp = bytes;
+(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0);
+ return (-1);
+ }
+ amt -= amount;
+ bytes += amount;
+ startsec += amount / secsize;
+
+ if (lverbose && (bytes >= (savbytes + 0x100000))) {
+ int nsecs = (bytes - savbytes) / secsize;
+ float fspeed;
+
+ gettimeofday(&tcur, (struct timezone *)0);
+ printf("\rTrack %02d: %4lld", track, bytes >> 20);
+ if (padsize > 0)
+ printf(" of %4lld MB", padsize >> 20);
+ else
+ printf(" MB");
+ printf(" pad written");
+ savbytes = (bytes >> 20) << 20;
+
+#ifdef BCAP
+ if (bsize > 0) { /* buffer size known */
+ usalp->silent++;
+ per = (*dp->cdr_buffer_cap)(usalp, (long *)0, &bfree);
+ usalp->silent--;
+ if (per >= 0) {
+ per = 100*(bsize - bfree) / bsize;
+ if ((bsize - bfree) <= amount || per <= 5)
+ dp->cdr_dstat->ds_buflow++;
+ if (per < (int)dp->cdr_dstat->ds_minbuf &&
+ (startsec*secsize) > bsize) {
+ dp->cdr_dstat->ds_minbuf = per;
+ }
+ printf(" [buf %3d%%]", per);
+#ifdef BCAPDBG
+ printf(" %3ld %3ld", bsize >> 10, bfree >> 10);
+#endif
+ }
+ }
+#endif
+ tlast.tv_sec = tcur.tv_sec - tlast.tv_sec;
+ tlast.tv_usec = tcur.tv_usec - tlast.tv_usec;
+ while (tlast.tv_usec < 0) {
+ tlast.tv_usec += 1000000;
+ tlast.tv_sec -= 1;
+ }
+ fspeed = (nsecs / secsps) /
+ (tlast.tv_sec * 1.0 + tlast.tv_usec * 0.000001);
+ if (fspeed > 999.0)
+ fspeed = 999.0;
+ printf(" %5.1fx", fspeed);
+ printf(".");
+ flush();
+ neednl = TRUE;
+ tlast = tcur;
+ }
+ } while (amt > 0);
+
+ if (bytesp)
+ *bytesp = bytes;
+ if (bytes == 0)
+ return (0);
+ return (bytes > 0 ? 1:-1);
+}
+
+#ifdef USE_WRITE_BUF
+int
+write_buf(SCSI *usalp, cdr_t *dp, track_t *trackp, char *bp, long startsec,
+ Llong amt, int secsize, BOOL dolast, Llong *bytesp)
+{
+ int track = trackp->trackno;
+ Llong bytes = 0;
+ Llong savbytes = 0;
+/* int secsize;*/
+ int secspt;
+ int bytespt;
+ int amount;
+ BOOL neednl = FALSE;
+ BOOL islast = FALSE;
+
+/* secsize = trackp->secsize;*/
+/* secspt = trackp->secspt;*/
+
+ secspt = bufsize/secsize;
+ secspt = min(255, secspt);
+ bytespt = secsize * secspt;
+
+/* fillbytes(buf, bytespt, '\0');*/
+
+ if ((amt >> 20) > 0) {
+ printf("\rTrack %02d: 0 of %4ld MB pad written.",
+ track, amt >> 20);
+ flush();
+ }
+ do {
+ if (amt < bytespt) {
+ bytespt = roundup(amt, secsize);
+ secspt = bytespt / secsize;
+ }
+ if (dolast && (amt - bytespt) <= 0)
+ islast = TRUE;
+
+ amount = write_secs(usalp, dp, bp, startsec, bytespt, secspt, islast);
+ if (amount < 0) {
+ printf("%swrite track data: error after %ld bytes\n",
+ neednl?"\n":"", bytes);
+ if (bytesp)
+ *bytesp = bytes;
+(*dp->cdr_buffer_cap)(usalp, (long *)0, (long *)0);
+ return (-1);
+ }
+ amt -= amount;
+ bytes += amount;
+ startsec += amount / secsize;
+
+ if (lverbose && (bytes >= (savbytes + 0x100000))) {
+ printf("\rTrack %02d: %3ld", track, bytes >> 20);
+ savbytes = (bytes >> 20) << 20;
+ flush();
+ neednl = TRUE;
+ }
+ } while (amt > 0);
+
+ if (bytesp)
+ *bytesp = bytes;
+ return (bytes);
+}
+#endif /* USE_WRITE_BUF */
+
+static void
+printdata(int track, track_t *trackp)
+{
+ if (trackp->itracksize >= 0) {
+ printf("Track %02d: data %4lld MB ",
+ track, (Llong)(trackp->itracksize >> 20));
+ } else {
+ printf("Track %02d: data unknown length",
+ track);
+ }
+ if (trackp->padsecs > 0) {
+ Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize;
+
+ if ((padbytes >> 20) > 0)
+ printf(" padsize: %4lld MB", (Llong)(padbytes >> 20));
+ else
+ printf(" padsize: %4lld KB", (Llong)(padbytes >> 10));
+ }
+ if (trackp->pregapsize != (trackp->flags & TI_DVD)? 0 : 150) {
+ printf(" pregapsize: %3ld", trackp->pregapsize);
+ }
+ if (xdebug)
+ printf(" START: %ld SECTORS: %ld INDEX0 %ld",
+ trackp->trackstart, trackp->tracksecs, trackp->index0start);
+ printf("\n");
+}
+
+static void
+printaudio(int track, track_t *trackp)
+{
+ if (trackp->itracksize >= 0) {
+ printf("Track %02d: audio %4lld MB (%02d:%02d.%02d) %spreemp%s%s",
+ track, (Llong)(trackp->itracksize >> 20),
+ minutes(trackp->itracksize),
+ seconds(trackp->itracksize),
+ hseconds(trackp->itracksize),
+ is_preemp(trackp) ? "" : "no ",
+ is_swab(trackp) ? " swab":"",
+ ((trackp->itracksize < 300L*trackp->isecsize) ||
+ (trackp->itracksize % trackp->isecsize)) &&
+ is_pad(trackp) ? " pad" : "");
+ } else {
+ printf("Track %02d: audio unknown length %spreemp%s%s",
+ track, is_preemp(trackp) ? "" : "no ",
+ is_swab(trackp) ? " swab":"",
+ (trackp->itracksize % trackp->isecsize) && is_pad(trackp) ? " pad" : "");
+ }
+ if (is_scms(trackp))
+ printf(" scms");
+ else if (is_copy(trackp))
+ printf(" copy");
+ else
+ printf(" ");
+
+ if (trackp->padsecs > 0) {
+ Llong padbytes = (Llong)trackp->padsecs * trackp->isecsize;
+
+ if ((padbytes >> 20) > 0)
+ printf(" padsize: %4lld MB", (Llong)(padbytes >> 20));
+ else
+ printf(" padsize: %4lld KB", (Llong)(padbytes >> 10));
+ printf(" (%02d:%02d.%02d)",
+ Sminutes(trackp->padsecs),
+ Sseconds(trackp->padsecs),
+ Shseconds(trackp->padsecs));
+ }
+ if (trackp->pregapsize != ((trackp->flags & TI_DVD)? 0 : 150) || xdebug > 0) {
+ printf(" pregapsize: %3ld", trackp->pregapsize);
+ }
+ if (xdebug)
+ printf(" START: %ld SECTORS: %ld INDEX0 %ld",
+ trackp->trackstart, trackp->tracksecs, trackp->index0start);
+ printf("\n");
+}
+
+static void
+checkfile(int track, track_t *trackp)
+{
+ if (trackp->itracksize > 0 &&
+ is_audio(trackp) &&
+ ((!is_shorttrk(trackp) &&
+ (trackp->itracksize < 300L*trackp->isecsize)) ||
+ (trackp->itracksize % trackp->isecsize)) &&
+ !is_pad(trackp)) {
+ errmsgno(EX_BAD, "Bad audio track size %lld for track %02d.\n",
+ (Llong)trackp->itracksize, track);
+ errmsgno(EX_BAD, "Audio tracks must be at least %ld bytes and a multiple of %d.\n",
+ 300L*trackp->isecsize, trackp->isecsize);
+
+ if (!is_shorttrk(trackp) && (trackp->itracksize < 300L*trackp->isecsize))
+ comerrno(EX_BAD, "See -shorttrack option.\n");
+ if (!is_pad(trackp) && (trackp->itracksize % trackp->isecsize))
+ comerrno(EX_BAD, "See -pad option.\n");
+ }
+
+ if (lverbose == 0 && xdebug == 0)
+ return;
+
+ if (is_audio(trackp))
+ printaudio(track, trackp);
+ else
+ printdata(track, trackp);
+}
+
+static int
+checkfiles(int tracks, track_t *trackp)
+{
+ int i;
+ int isaudio = 1;
+ int starttrack = 1;
+ int endtrack = tracks;
+
+ if (xdebug) {
+ /*
+ * Include Lead-in & Lead-out.
+ */
+ starttrack--;
+ endtrack++;
+ }
+ for (i = starttrack; i <= endtrack; i++) {
+ if (!is_audio(&trackp[i]))
+ isaudio = 0;
+ if (xdebug)
+ printf("SECTYPE %X ", trackp[i].sectype);
+ checkfile(i, &trackp[i]);
+ }
+ return (isaudio);
+}
+
+static void
+setleadinout(int tracks, track_t *trackp)
+{
+ /*
+ * Set some values for track 0 (the lead-in)
+ */
+ if (!is_clone(&trackp[0])) {
+ trackp[0].sectype = trackp[1].sectype;
+ trackp[0].dbtype = trackp[1].dbtype;
+ trackp[0].dataoff = trackp[1].dataoff;
+
+ /*
+ * XXX Which other flags should be copied to Track 0 ?
+ */
+ if (is_audio(&trackp[1]))
+ trackp[0].flags |= TI_AUDIO;
+ }
+
+ /*
+ * Set some values for track 0xAA (the lead-out)
+ */
+ trackp[tracks+1].pregapsize = 0;
+ trackp[tracks+1].isecsize = trackp[tracks].isecsize;
+ trackp[tracks+1].secsize = trackp[tracks].secsize;
+
+ if (!is_clone(&trackp[0])) {
+ trackp[tracks+1].tracktype = trackp[tracks].tracktype;
+ trackp[tracks+1].sectype = trackp[tracks].sectype;
+ trackp[tracks+1].dbtype = trackp[tracks].dbtype;
+ trackp[tracks+1].dataoff = trackp[tracks].dataoff;
+ }
+
+ trackp[tracks+1].flags = trackp[tracks].flags;
+}
+
+static void
+setpregaps(int tracks, track_t *trackp)
+{
+ int i;
+ int sectype;
+ long pregapsize;
+ track_t *tp;
+
+ sectype = trackp[1].sectype;
+ sectype &= ST_MASK;
+
+ for (i = 1; i <= tracks; i++) {
+ tp = &trackp[i];
+ if (tp->pregapsize == -1L) {
+ tp->pregapsize = 150; /* Default CD Pre GAP*/
+ if (trackp->flags & TI_DVD) {
+ tp->pregapsize = 0;
+ } else if (sectype != (tp->sectype & ST_MASK)) {
+ tp->pregapsize = 255; /* Pre GAP is 255 */
+ tp->flags &= ~TI_PREGAP;
+ }
+ }
+ sectype = tp->sectype & ST_MASK; /* Save old sectype */
+ }
+ trackp[tracks+1].pregapsize = 0;
+ trackp[tracks+1].index0start = 0;
+
+ for (i = 1; i <= tracks; i++) {
+ /*
+ * index0start is set below tracksecks if this track contains
+ * the pregap (index 0) of the next track.
+ */
+ trackp[i].index0start = trackp[i].tracksecs;
+
+ pregapsize = trackp[i+1].pregapsize;
+ if (is_pregap(&trackp[i+1]) && pregapsize > 0)
+ trackp[i].index0start -= pregapsize;
+ }
+}
+
+/*
+ * Check total size of the medium
+ */
+static long
+checktsize(int tracks, track_t *trackp)
+{
+ int i;
+ Llong curr;
+ Llong total = -150; /* CD track #1 pregap compensation */
+ Ullong btotal;
+ track_t *tp;
+
+ if (trackp->flags & TI_DVD)
+ total = 0;
+ for (i = 1; i <= tracks; i++) {
+ tp = &trackp[i];
+ if (!is_pregap(tp))
+ total += tp->pregapsize;
+
+ if (lverbose > 1) {
+ printf("track: %d start: %lld pregap: %ld\n",
+ i, total, tp->pregapsize);
+ }
+ tp->trackstart = total;
+ if (tp->itracksize >= 0) {
+ curr = (tp->itracksize + (tp->isecsize-1)) / tp->isecsize;
+ curr += tp->padsecs;
+ /*
+ * Minimum track size is 4s
+ */
+ if (!is_shorttrk(tp) && curr < 300)
+ curr = 300;
+ if ((trackp->flags & TI_DVD) == 0) {
+ /*
+ * XXX Was passiert hier bei is_packet() ???
+ */
+ if (is_tao(tp) && !is_audio(tp)) {
+ curr += 2;
+ }
+ }
+ total += curr;
+ } else if (is_sao(tp) || is_raw(tp)) {
+ errmsgno(EX_BAD, "Track %d has unknown length.\n", i);
+ comerrno(EX_BAD,
+ "Use tsize= option in %s mode to specify track size.\n",
+ is_sao(tp) ? "SAO" : "RAW");
+ }
+ }
+ tp = &trackp[i];
+ tp->trackstart = total;
+ tp->tracksecs = 6750; /* Size of first session Lead-Out */
+ if (!lverbose)
+ return (total);
+
+ if (trackp->flags & TI_DVD)
+ btotal = (Ullong)total * 2048;
+ else
+ btotal = (Ullong)total * 2352;
+/* XXX CD Sector Size ??? */
+ if (tracks > 0) {
+ if (trackp->flags & TI_DVD) {
+ printf("Total size: %4llu MB = %lld sectors\n",
+ btotal >> 20, total);
+ } else {
+ printf("Total size: %4llu MB (%02d:%02d.%02d) = %lld sectors\n",
+ btotal >> 20,
+ minutes(btotal),
+ seconds(btotal),
+ hseconds(btotal), total);
+ btotal += 150 * 2352;
+ printf("Lout start: %4llu MB (%02d:%02d/%02d) = %lld sectors\n",
+ btotal >> 20,
+ minutes(btotal),
+ seconds(btotal),
+ frames(btotal), total);
+ }
+ }
+ return (total);
+}
+
+static void
+opentracks(track_t *trackp)
+{
+ track_t *tp;
+ int i;
+ int tracks = trackp[0].tracks;
+
+ Llong tracksize;
+ int secsize;
+
+ for (i = 1; i <= tracks; i++) {
+ tp = &trackp[i];
+
+ if (auinfosize(tp->filename, tp)) {
+ /*
+ * open stdin
+ */
+ tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0);
+ } else if (strcmp("-", tp->filename) == 0) {
+ /*
+ * open stdin
+ */
+ tp->xfp = xopen(NULL, O_RDONLY|O_BINARY, 0);
+ } else {
+ if ((tp->xfp = xopen(tp->filename,
+ O_RDONLY|O_BINARY, 0)) == NULL) {
+ comerr("Cannot open '%s'.\n", tp->filename);
+ }
+ }
+
+ checksize(tp);
+ tracksize = tp->itracksize;
+ secsize = tp->isecsize;
+ if (!is_shorttrk(tp) &&
+ tracksize > 0 && (tracksize / secsize) < 300) {
+
+ tracksize = roundup(tracksize, secsize);
+ if ((tp->padsecs +
+ (tracksize / secsize)) < 300) {
+ tp->padsecs =
+ 300 - tracksize / secsize;
+ }
+ if (xdebug) {
+ printf("TRACK %d SECTORS: %ld",
+ i, tp->tracksecs);
+ printf(" pasdize %lld (%ld sectors)\n",
+ (Llong)tp->padsecs * secsize,
+ tp->padsecs);
+ }
+ }
+#ifdef AUINFO
+ if (tp->flags & TI_USEINFO) {
+ auinfo(tp->filename, i, trackp);
+ if (lverbose > 0 && i == 1)
+ printf("pregap1: %ld\n", trackp[1].pregapsize);
+ }
+#endif
+ /*
+ * tracksecks is total numbers of sectors in track (starting from
+ * index 0).
+ */
+ if (tp->padsecs > 0)
+ tp->tracksecs += tp->padsecs;
+
+ if (debug) {
+ printf("File: '%s' itracksize: %lld isecsize: %d tracktype: %d = %s sectype: %X = %s dbtype: %s flags %X\n",
+ tp->filename, (Llong)tp->itracksize,
+ tp->isecsize,
+ tp->tracktype & TOC_MASK, toc2name[tp->tracktype & TOC_MASK],
+ tp->sectype, st2name[tp->sectype & ST_MASK], db2name[tp->dbtype], tp->flags);
+ }
+ }
+}
+
+static void
+checksize(track_t *trackp)
+{
+ struct stat st;
+ Llong lsize;
+ int f = -1;
+
+ if (trackp->xfp != NULL)
+ f = xfileno(trackp->xfp);
+
+ /*
+ * If the current input file is a regular file and
+ * 'padsize=' has not been specified,
+ * use fstat() or file parser to get the size of the file.
+ */
+ if (trackp->itracksize < 0 && (trackp->flags & TI_ISOSIZE) != 0) {
+ lsize = isosize(f);
+ trackp->itracksize = lsize;
+ if (trackp->itracksize != lsize)
+ comerrno(EX_BAD, "This OS cannot handle large ISO-9660 images.\n");
+ }
+ if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) {
+ lsize = ausize(f);
+ trackp->itracksize = lsize;
+ if (trackp->itracksize != lsize)
+ comerrno(EX_BAD, "This OS cannot handle large audio images.\n");
+ }
+ if (trackp->itracksize < 0 && (trackp->flags & TI_NOAUHDR) == 0) {
+ lsize = wavsize(f);
+ trackp->itracksize = lsize;
+ if (trackp->itracksize != lsize)
+ comerrno(EX_BAD, "This OS cannot handle large WAV images.\n");
+ if (trackp->itracksize > 0) /* Force little endian input */
+ trackp->flags |= TI_SWAB;
+ }
+ if (trackp->itracksize == AU_BAD_CODING) {
+ comerrno(EX_BAD, "Inappropriate audio coding in '%s'.\n",
+ trackp->filename);
+ }
+ if (trackp->itracksize < 0 &&
+ fstat(f, &st) >= 0 && S_ISREG(st.st_mode)) {
+ trackp->itracksize = st.st_size;
+ }
+ if (trackp->itracksize >= 0) {
+ /*
+ * We do not allow cdrecord to start if itracksize is not
+ * a multiple of isecsize or we are allowed to pad to secsize via -pad.
+ * For this reason, we may safely always assume padding.
+ */
+ trackp->tracksecs = (trackp->itracksize + trackp->isecsize -1) / trackp->isecsize;
+ trackp->tracksize = (trackp->itracksize / trackp->isecsize) * trackp->secsize
+ + trackp->itracksize % trackp->isecsize;
+ } else {
+ trackp->tracksecs = -1L;
+ }
+}
+
+static BOOL
+checkdsize(SCSI *usalp, cdr_t *dp, long tsize, int flags)
+{
+ long startsec = 0L;
+ long endsec = 0L;
+ dstat_t *dsp = dp->cdr_dstat;
+ int profile;
+
+ usalp->silent++;
+ (*dp->cdr_next_wr_address)(usalp, (track_t *)0, &startsec);
+ usalp->silent--;
+
+ /*
+ * This only should happen when the drive is currently in SAO mode.
+ * We rely on the drive being in TAO mode, a negative value for
+ * startsec is not correct here it may be caused by bad firmware or
+ * by a drive in SAO mode. In SAO mode the drive will report the
+ * pre-gap as part of the writable area.
+ */
+ if (startsec < 0)
+ startsec = 0;
+
+ /*
+ * Size limitations (sectors) for CD's:
+ *
+ * 404850 == 90 min Red book calls this the
+ * first negative time
+ * allows lead out start up to
+ * block 404700
+ *
+ * 449850 == 100 min This is the first time that
+ * is no more representable
+ * in a two digit BCD number.
+ * allows lead out start up to
+ * block 449700
+ *
+ * ~540000 == 120 min The largest CD ever made.
+ *
+ * ~650000 == 1.3 GB a Double Density (DD) CD.
+ */
+
+ endsec = startsec + tsize;
+ dsp->ds_startsec = startsec;
+ dsp->ds_endsec = endsec;
+
+
+ if (dsp->ds_maxblocks > 0) {
+ /*
+ * dsp->ds_maxblocks > 0 (disk capacity is known).
+ */
+ if (lverbose)
+ printf("Blocks total: %ld Blocks current: %ld Blocks remaining: %ld\n",
+ (long)dsp->ds_maxblocks,
+ (long)dsp->ds_maxblocks - startsec,
+ (long)dsp->ds_maxblocks - endsec);
+
+ if (endsec > dsp->ds_maxblocks) {
+ if (dsp->ds_flags & DSF_DVD) { /* A DVD and not a CD */
+ /*
+ * There is no overburning on DVD...
+ */
+ errmsgno(EX_BAD,
+ "Data does not fit on current disk.\n");
+ goto toolarge;
+ }
+ errmsgno(EX_BAD,
+ "WARNING: Data may not fit on current disk.\n");
+
+ /* XXX Check for flags & CDR_NO_LOLIMIT */
+/* goto toolarge;*/
+ }
+ if (lverbose && dsp->ds_maxrblocks > 0)
+ printf("RBlocks total: %ld RBlocks current: %ld RBlocks remaining: %ld\n",
+ (long)dsp->ds_maxrblocks,
+ (long)dsp->ds_maxrblocks - startsec,
+ (long)dsp->ds_maxrblocks - endsec);
+ if (dsp->ds_maxrblocks > 0 && endsec > dsp->ds_maxrblocks) {
+ errmsgno(EX_BAD,
+ "Data does not fit on current disk.\n");
+ goto toolarge;
+ }
+ if ((endsec > dsp->ds_maxblocks && endsec > 404700) ||
+ (dsp->ds_maxrblocks > 404700 && 449850 > dsp->ds_maxrblocks)) {
+ /*
+ * Assume that this must be a CD and not a DVD.
+ * So this is a non Red Book compliant CD with a
+ * capacity between 90 and 99 minutes.
+ */
+ if (dsp->ds_maxrblocks > 404700)
+ printf("RedBook total: %ld RedBook current: %ld RedBook remaining: %ld\n",
+ 404700L,
+ 404700L - startsec,
+ 404700L - endsec);
+ if (endsec > dsp->ds_maxblocks && endsec > 404700) {
+ if ((flags & (F_IGNSIZE|F_FORCE)) == 0) {
+ errmsgno(EX_BAD,
+ "Notice: Most recorders cannot write CD's >= 90 minutes.\n");
+ errmsgno(EX_BAD,
+ "Notice: Use -ignsize option to allow >= 90 minutes.\n");
+ }
+ goto toolarge;
+ }
+ }
+ } else {
+ /*
+ * dsp->ds_maxblocks == 0 (disk capacity is unknown).
+ */
+ profile = dp->profile;
+ if (endsec >= (4200000)) {
+ errmsgno(EX_BAD,
+ "ERROR: Could not manage to find medium size, and more than 8.0 GB of data.\n");
+ goto toolarge;
+ } else if (profile != 0x2B) {
+ if (endsec >= (2300000)) {
+ errmsgno(EX_BAD,
+ "ERROR: Could not manage to find medium size, and more than 4.3 GB of data for a non dual layer disc.\n");
+ goto toolarge;
+ } else if (endsec >= (405000-300)) { /*<90 min disk or DVD*/
+ errmsgno(EX_BAD,
+ "WARNING: Could not manage to find medium size, and more than 90 mins of data.\n");
+ } else if (endsec >= (333000-150)) { /* 74 min disk*/
+ errmsgno(EX_BAD,
+ "WARNING: Data may not fit on standard 74min disk.\n");
+ }
+ }
+ }
+ if (dsp->ds_maxblocks <= 0 || endsec <= dsp->ds_maxblocks)
+ return (TRUE);
+ /* FALLTHROUGH */
+toolarge:
+ if (dsp->ds_maxblocks > 0 && endsec > dsp->ds_maxblocks) {
+ if ((flags & (F_OVERBURN|F_IGNSIZE|F_FORCE)) != 0) {
+ if (dsp->ds_flags & DSF_DVD) { /* A DVD and not a CD */
+ errmsgno(EX_BAD,
+ "Notice: -overburn is not expected to work with DVD media.\n");
+ }
+ errmsgno(EX_BAD,
+ "Notice: Overburning active. Trying to write more than the official disk capacity.\n");
+ return (TRUE);
+ } else {
+ if ((dsp->ds_flags & DSF_DVD) == 0) { /* A CD and not a DVD */
+ errmsgno(EX_BAD,
+ "Notice: Use -overburn option to write more than the official disk capacity.\n");
+ errmsgno(EX_BAD,
+ "Notice: Most CD-writers do overburning only on SAO or RAW mode.\n");
+ }
+ return (FALSE);
+ }
+ }
+ if (dsp->ds_maxblocks < 449850) {
+ if ((dsp->ds_flags & DSF_DVD) == 0) { /* A CD and not a DVD */
+ if (endsec <= dsp->ds_maxblocks)
+ return (TRUE);
+ errmsgno(EX_BAD, "Cannot write more than remaining DVD capacity.\n");
+ return (FALSE);
+ }
+ /*
+ * Assume that this must be a CD and not a DVD.
+ */
+ if (endsec > 449700) {
+ errmsgno(EX_BAD, "Cannot write CD's >= 100 minutes.\n");
+ return (FALSE);
+ }
+ }
+ if ((flags & (F_IGNSIZE|F_FORCE)) != 0)
+ return (TRUE);
+ return (FALSE);
+}
+
+static void
+raise_fdlim()
+{
+#ifdef RLIMIT_NOFILE
+
+ struct rlimit rlim;
+
+ /*
+ * Set max # of file descriptors to be able to hold all files open
+ */
+ getrlimit(RLIMIT_NOFILE, &rlim);
+ if (rlim.rlim_cur >= (MAX_TRACK + 10))
+ return;
+
+ rlim.rlim_cur = MAX_TRACK + 10;
+ if (rlim.rlim_cur > rlim.rlim_max)
+ errmsgno(EX_BAD,
+ "Warning: low file descriptor limit (%lld)\n",
+ (Llong)rlim.rlim_max);
+ setrlimit(RLIMIT_NOFILE, &rlim);
+
+#endif /* RLIMIT_NOFILE */
+}
+
+static void
+raise_memlock()
+{
+#ifdef RLIMIT_MEMLOCK
+ struct rlimit rlim;
+
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+
+ if (setrlimit(RLIMIT_MEMLOCK, &rlim) < 0)
+ errmsg("Warning: Cannot raise RLIMIT_MEMLOCK limits.\n");
+#endif /* RLIMIT_MEMLOCK */
+}
+
+char *opts =
+"help,version,checkdrive,prcap,inq,devices,scanbus,reset,abort,overburn,ignsize,useinfo,dev*,timeout#,driver*,driveropts*,setdropts,tsize&,padsize&,pregap&,defpregap&,speed#,load,lock,eject,dummy,msinfo,toc,atip,multi,fix,nofix,waiti,immed,debug#,d+,kdebug#,kd#,verbose+,v+,Verbose+,V+,x+,xd#,silent,s,audio,data,mode2,xa,xa1,xa2,xamix,cdi,isosize,nopreemp,preemp,nocopy,copy,nopad,pad,swab,fs&,ts&,blank&,format,formattype&,pktsize#,packet,noclose,force,tao,dao,sao,raw,raw96r,raw96p,raw16,clone,scms,isrc*,mcn*,index*,cuefile*,textfile*,text,shorttrack,noshorttrack,gracetime#,minbuf#,msifile*";
+
+/*
+ * Defines used to find whether a write mode has been specified.
+ */
+#define M_TAO 1 /* Track at Once mode */
+#define M_SAO 2 /* Session at Once mode (also known as DAO) */
+#define M_RAW 4 /* Raw mode */
+#define M_PACKET 8 /* Packed mode */
+static int
+gargs(int ac, char **av, int *tracksp, track_t *trackp, char **devp,
+ int *timeoutp, cdr_t **dpp, int *speedp, long *flagsp, int *blankp,
+ int *formatp)
+{
+ int cac;
+ char * const*cav;
+ char *driver = NULL;
+ char *dev = NULL;
+ char *isrc = NULL;
+ char *mcn = NULL;
+ char *tindex = NULL;
+ char *cuefile = NULL;
+ char *textfile = NULL;
+ long bltype = -1;
+ int doformat = 0;
+ int formattype = -1;
+ Llong tracksize;
+ Llong padsize;
+ long pregapsize;
+ long defpregap = -1L;
+ int pktsize;
+ int speed = -1;
+ int help = 0;
+ int version = 0;
+ int checkdrive = 0;
+ int setdropts = 0;
+ int prcap = 0;
+ int inq = 0;
+ int scanbus = 0;
+ int reset = 0;
+ int doabort = 0;
+ int overburn = 0;
+ int ignsize = 0;
+ int useinfo = 0;
+ int load = 0;
+ int lock = 0;
+ int eject = 0;
+ int dummy = 0;
+ int msinfo = 0;
+ int toc = 0;
+ int atip = 0;
+ int multi = 0;
+ int fix = 0;
+ int nofix = 0;
+ int waiti = 0;
+ int immed = 0;
+ int audio;
+ int autoaudio = 0;
+ int data;
+ int mode2;
+ int xa;
+ int xa1;
+ int xa2;
+ int xamix;
+ int cdi;
+ int isize;
+ int ispacket = 0;
+ int noclose = 0;
+ int force = 0;
+ int tao = 0;
+ int dao = 0;
+ int raw = 0;
+ int raw96r = 0;
+ int raw96p = 0;
+ int raw16 = 0;
+ int clone = 0;
+ int scms = 0;
+ int preemp = 0;
+ int nopreemp;
+ int copy = 0;
+ int nocopy;
+ int pad = 0;
+ int bswab = 0;
+ int nopad;
+ int usetext = 0;
+ int shorttrack = 0;
+ int noshorttrack;
+ int flags;
+ int tracks = *tracksp;
+ int tracktype = TOC_ROM;
+/* int sectype = ST_ROM_MODE1 | ST_MODE_1;*/
+ int sectype = SECT_ROM;
+ int dbtype = DB_ROM_MODE1;
+ int secsize = DATA_SEC_SIZE;
+ int dataoff = 16;
+ int ga_ret;
+ int wm = 0;
+
+ trackp[0].flags |= TI_TAO;
+ trackp[1].pregapsize = -1;
+ *flagsp |= F_WRITE;
+
+ cac = --ac;
+ cav = ++av;
+ for (; ; cac--, cav++) {
+ tracksize = (Llong)-1L;
+ padsize = (Llong)0L;
+ pregapsize = defpregap;
+ audio = data = mode2 = xa = xa1 = xa2 = xamix = cdi = 0;
+ isize = nopreemp = nocopy = nopad = noshorttrack = 0;
+ pktsize = 0;
+ isrc = NULL;
+ tindex = NULL;
+ /*
+ * Get options up to next file type arg.
+ */
+ if ((ga_ret = getargs(&cac, &cav, opts,
+ &help, &version, &checkdrive, &prcap,
+ &inq, &scandevs, &scanbus, &reset, &doabort, &overburn, &ignsize,
+ &useinfo,
+ devp, timeoutp, &driver, &driveropts, &setdropts,
+ getllnum, &tracksize,
+ getllnum, &padsize,
+ getnum, &pregapsize,
+ getnum, &defpregap,
+ &speed,
+ &load, &lock,
+ &eject, &dummy, &msinfo, &toc, &atip,
+ &multi, &fix, &nofix, &waiti, &immed,
+ &debug, &debug,
+ &kdebug, &kdebug,
+ &lverbose, &lverbose,
+ &scsi_verbose, &scsi_verbose,
+ &xdebug, &xdebug,
+ &silent, &silent,
+ &audio, &data, &mode2,
+ &xa, &xa1, &xa2, &xamix, &cdi,
+ &isize,
+ &nopreemp, &preemp,
+ &nocopy, &copy,
+ &nopad, &pad, &bswab, getnum, &fs, getnum, &bufsize,
+ getbltype, &bltype, &doformat, getformattype, &formattype, &pktsize,
+ &ispacket, &noclose, &force,
+ &tao, &dao, &dao, &raw, &raw96r, &raw96p, &raw16,
+ &clone,
+ &scms, &isrc, &mcn, &tindex,
+ &cuefile, &textfile, &usetext,
+ &shorttrack, &noshorttrack,
+ &gracetime, &dminbuf, &msifile)) < 0) {
+ errmsgno(EX_BAD, "Bad Option: %s.\n", cav[0]);
+ susage(EX_BAD);
+ }
+ if (help)
+ usage(0);
+ if (tracks == 0) {
+ if (driver)
+ set_cdrcmds(driver, dpp);
+ if (version)
+ *flagsp |= F_VERSION;
+ if (checkdrive)
+ *flagsp |= F_CHECKDRIVE;
+ if (prcap)
+ *flagsp |= F_PRCAP;
+ if (inq)
+ *flagsp |= F_INQUIRY;
+ if (scanbus || scandevs) /* scandevs behaves similarly WRT in the legacy code, just the scan operation is different */
+ *flagsp |= F_SCANBUS;
+ if (reset)
+ *flagsp |= F_RESET;
+ if (doabort)
+ *flagsp |= F_ABORT;
+ if (overburn)
+ *flagsp |= F_OVERBURN;
+ if (ignsize)
+ *flagsp |= F_IGNSIZE;
+ if (load)
+ *flagsp |= F_LOAD;
+ if (lock)
+ *flagsp |= F_DLCK;
+ if (eject)
+ *flagsp |= F_EJECT;
+ if (dummy)
+ *flagsp |= F_DUMMY;
+ if (setdropts)
+ *flagsp |= F_SETDROPTS;
+ if(msifile)
+ msinfo++;
+ if (msinfo)
+ *flagsp |= F_MSINFO;
+ if (toc) {
+ *flagsp |= F_TOC;
+ *flagsp &= ~F_WRITE;
+ }
+ if (atip) {
+ *flagsp |= F_PRATIP;
+ *flagsp &= ~F_WRITE;
+ }
+ if (multi) {
+ /*
+ * 2048 Bytes user data
+ */
+ *flagsp |= F_MULTI;
+ tracktype = TOC_XA2;
+ sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
+ sectype = SECT_MODE_2_F1;
+ dbtype = DB_XA_MODE2; /* XXX -multi nimmt DB_XA_MODE2_F1 !!! */
+ secsize = DATA_SEC_SIZE; /* 2048 */
+ dataoff = 24;
+ }
+ if (fix)
+ *flagsp |= F_FIX;
+ if (nofix)
+ *flagsp |= F_NOFIX;
+ if (waiti)
+ *flagsp |= F_WAITI;
+ if (immed)
+ *flagsp |= F_IMMED;
+ if (force)
+ *flagsp |= F_FORCE;
+
+ if (bltype >= 0) {
+ *flagsp |= F_BLANK;
+ *blankp = bltype;
+ }
+ if (doformat > 0) {
+ *flagsp |= F_FORMAT;
+ *formatp |= FULL_FORMAT;
+ }
+ if (formattype >= 0) {
+ *flagsp |= F_FORMAT;
+ *formatp |= formattype;
+ }
+ if (ispacket)
+ wm |= M_PACKET;
+ if (tao)
+ wm |= M_TAO;
+ if (dao) {
+ *flagsp |= F_SAO;
+ trackp[0].flags &= ~TI_TAO;
+ trackp[0].flags |= TI_SAO;
+ wm |= M_SAO;
+
+ } else if ((raw == 0) && (raw96r + raw96p + raw16) > 0)
+ raw = 1;
+ if ((raw != 0) && (raw96r + raw96p + raw16) == 0)
+ raw96r = 1;
+ if (raw96r) {
+ if (!dao)
+ *flagsp |= F_RAW;
+ trackp[0].flags &= ~TI_TAO;
+ trackp[0].flags |= TI_RAW;
+ trackp[0].flags |= TI_RAW96R;
+ wm |= M_RAW;
+ }
+ if (raw96p) {
+ if (!dao)
+ *flagsp |= F_RAW;
+ trackp[0].flags &= ~TI_TAO;
+ trackp[0].flags |= TI_RAW;
+ wm |= M_RAW;
+ }
+ if (raw16) {
+ if (!dao)
+ *flagsp |= F_RAW;
+ trackp[0].flags &= ~TI_TAO;
+ trackp[0].flags |= TI_RAW;
+ trackp[0].flags |= TI_RAW16;
+ wm |= M_RAW;
+ }
+ if (mcn) {
+#ifdef AUINFO
+ setmcn(mcn, &trackp[0]);
+#else
+ trackp[0].isrc = malloc(16);
+ fillbytes(trackp[0].isrc, 16, '\0');
+ strncpy(trackp[0].isrc, mcn, 13);
+#endif
+ mcn = NULL;
+ }
+ if ((raw96r + raw96p + raw16) > 1) {
+ errmsgno(EX_BAD, "Too many raw modes.\n");
+ comerrno(EX_BAD, "Only one of -raw16, -raw96p, -raw96r allowed.\n");
+ }
+ if ((tao + ispacket + dao + raw) > 1) {
+ errmsgno(EX_BAD, "Too many write modes.\n");
+ comerrno(EX_BAD, "Only one of -packet, -dao, -raw allowed.\n");
+ }
+ if (dao && (raw96r + raw96p + raw16) > 0) {
+ if (raw16)
+ comerrno(EX_BAD, "SAO RAW writing does not allow -raw16.\n");
+ if (!clone)
+ comerrno(EX_BAD, "SAO RAW writing only makes sense in clone mode.\n");
+#ifndef CLONE_WRITE
+ comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n");
+#endif
+ comerrno(EX_BAD, "SAO RAW writing not yet implemented.\n");
+ }
+ if (clone) {
+ *flagsp |= F_CLONE;
+ trackp[0].flags |= TI_CLONE;
+#ifndef CLONE_WRITE
+ comerrno(EX_BAD, "Clone writing not compiled in.\n");
+#endif
+ }
+ if (textfile) {
+ if (!checktextfile(textfile)) {
+ if ((*flagsp & F_WRITE) != 0) {
+ comerrno(EX_BAD,
+ "Cannot use '%s' as CD-Text file.\n",
+ textfile);
+ }
+ }
+ if ((*flagsp & F_WRITE) != 0) {
+ if ((dao + raw96r + raw96p) == 0)
+ comerrno(EX_BAD,
+ "CD-Text needs -dao, -raw96r or -raw96p.\n");
+ }
+ trackp[0].flags |= TI_TEXT;
+ }
+ version = checkdrive = prcap = inq = scanbus = reset = doabort =
+ overburn = ignsize =
+ load = lock = eject = dummy = msinfo = toc = atip = multi = fix = nofix =
+ waiti = immed = force = dao = setdropts = 0;
+ raw96r = raw96p = raw16 = clone = 0;
+ } else if ((version + checkdrive + prcap + inq + scanbus +
+ reset + doabort + overburn + ignsize +
+ load + lock + eject + dummy + msinfo + toc + atip + multi + fix + nofix +
+ waiti + immed + force + dao + setdropts +
+ raw96r + raw96p + raw16 + clone) > 0 ||
+ mcn != NULL)
+ comerrno(EX_BAD, "Badly placed option. Global options must be before any track.\n");
+
+ if (nopreemp)
+ preemp = 0;
+ if (nocopy)
+ copy = 0;
+ if (nopad)
+ pad = 0;
+ if (noshorttrack)
+ shorttrack = 0;
+
+ if ((audio + data + mode2 + xa + xa1 + xa2 + xamix) > 1) {
+ errmsgno(EX_BAD, "Too many types for track %d.\n", tracks+1);
+ comerrno(EX_BAD, "Only one of -audio, -data, -mode2, -xa, -xa1, -xa2, -xamix allowed.\n");
+ }
+ if (ispacket && audio) {
+ comerrno(EX_BAD, "Audio data cannot be written in packet mode.\n");
+ }
+ /*
+ * Check whether the next argument is a file type arg.
+ * If this is true, then we got a track file name.
+ * If getargs() did previously return NOTAFLAG, we may have hit
+ * an argument that has been escaped via "--", so we may not
+ * call getfiles() again in this case. If we would call
+ * getfiles() and the current arg has been escaped and looks
+ * like an option, a call to getfiles() would skip it.
+ */
+ if (ga_ret != NOTAFLAG)
+ ga_ret = getfiles(&cac, &cav, opts);
+ if (autoaudio) {
+ autoaudio = 0;
+ tracktype = TOC_ROM;
+ sectype = ST_ROM_MODE1 | ST_MODE_1;
+ sectype = SECT_ROM;
+ dbtype = DB_ROM_MODE1;
+ secsize = DATA_SEC_SIZE; /* 2048 */
+ dataoff = 16;
+ }
+ if (ga_ret == NOTAFLAG && (is_auname(cav[0]) || is_wavname(cav[0]))) {
+ /*
+ * We got a track and autodetection decided that it
+ * is an audio track.
+ */
+ autoaudio++;
+ audio++;
+ }
+ if (data) {
+ /*
+ * 2048 Bytes user data
+ */
+ tracktype = TOC_ROM;
+ sectype = ST_ROM_MODE1 | ST_MODE_1;
+ sectype = SECT_ROM;
+ dbtype = DB_ROM_MODE1;
+ secsize = DATA_SEC_SIZE; /* 2048 */
+ dataoff = 16;
+ }
+ if (mode2) {
+ /*
+ * 2336 Bytes user data
+ */
+ tracktype = TOC_ROM;
+ sectype = ST_ROM_MODE2 | ST_MODE_2;
+ sectype = SECT_MODE_2;
+ dbtype = DB_ROM_MODE2;
+ secsize = MODE2_SEC_SIZE; /* 2336 */
+ dataoff = 16;
+ }
+ if (audio) {
+ /*
+ * 2352 Bytes user data
+ */
+ tracktype = TOC_DA;
+ sectype = preemp ? ST_AUDIO_PRE : ST_AUDIO_NOPRE;
+ sectype |= ST_MODE_AUDIO;
+ sectype = SECT_AUDIO;
+ if (preemp)
+ sectype |= ST_PREEMPMASK;
+ dbtype = DB_RAW;
+ secsize = AUDIO_SEC_SIZE; /* 2352 */
+ dataoff = 0;
+ }
+ if (xa) {
+ /*
+ * 2048 Bytes user data
+ */
+ if (tracktype != TOC_CDI)
+ tracktype = TOC_XA2;
+ sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
+ sectype = SECT_MODE_2_F1;
+ dbtype = DB_XA_MODE2;
+ secsize = DATA_SEC_SIZE; /* 2048 */
+ dataoff = 24;
+ }
+ if (xa1) {
+ /*
+ * 8 Bytes subheader + 2048 Bytes user data
+ */
+ if (tracktype != TOC_CDI)
+ tracktype = TOC_XA2;
+ sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_1;
+ sectype = SECT_MODE_2_F1;
+ dbtype = DB_XA_MODE2_F1;
+ secsize = 2056;
+ dataoff = 16;
+ }
+ if (xa2) {
+ /*
+ * 2324 Bytes user data
+ */
+ if (tracktype != TOC_CDI)
+ tracktype = TOC_XA2;
+ sectype = ST_ROM_MODE2 | ST_MODE_2_FORM_2;
+ sectype = SECT_MODE_2_F2;
+ dbtype = DB_XA_MODE2_F2;
+ secsize = 2324;
+ dataoff = 24;
+ }
+ if (xamix) {
+ /*
+ * 8 Bytes subheader + 2324 Bytes user data
+ */
+ if (tracktype != TOC_CDI)
+ tracktype = TOC_XA2;
+ sectype = ST_ROM_MODE2 | ST_MODE_2_MIXED;
+ sectype = SECT_MODE_2_MIX;
+ dbtype = DB_XA_MODE2_MIX;
+ secsize = 2332;
+ dataoff = 16;
+ }
+ if (cdi) {
+ tracktype = TOC_CDI;
+ }
+ if (tracks == 0) {
+ trackp[0].tracktype = tracktype;
+ trackp[0].dbtype = dbtype;
+ trackp[0].isecsize = secsize;
+ trackp[0].secsize = secsize;
+ if ((*flagsp & F_RAW) != 0) {
+ trackp[0].secsize = is_raw16(&trackp[0]) ?
+ RAW16_SEC_SIZE:RAW96_SEC_SIZE;
+ }
+ if ((*flagsp & F_DUMMY) != 0)
+ trackp[0].tracktype |= TOCF_DUMMY;
+ if ((*flagsp & F_MULTI) != 0)
+ trackp[0].tracktype |= TOCF_MULTI;
+ }
+
+ flags = trackp[0].flags;
+
+ if ((sectype & ST_AUDIOMASK) != 0)
+ flags |= TI_AUDIO;
+ if (isize) {
+ flags |= TI_ISOSIZE;
+ if ((*flagsp & F_MULTI) != 0)
+ comerrno(EX_BAD, "Cannot get isosize for multi session disks.\n");
+ /*
+ * As we do not get the padding from the ISO-9660
+ * formatting utility, we need to force padding here.
+ */
+ flags |= TI_PAD;
+ if (padsize == (Llong)0L)
+ padsize = (Llong)PAD_SIZE;
+ }
+
+ if ((flags & TI_AUDIO) != 0) {
+ if (preemp)
+ flags |= TI_PREEMP;
+ if (copy)
+ flags |= TI_COPY;
+ if (scms)
+ flags |= TI_SCMS;
+ }
+ if (pad || ((flags & TI_AUDIO) == 0 && padsize > (Llong)0L)) {
+ flags |= TI_PAD;
+ if ((flags & TI_AUDIO) == 0 && padsize == (Llong)0L)
+ padsize = (Llong)PAD_SIZE;
+ }
+ if (shorttrack && (*flagsp & (F_SAO|F_RAW)) != 0)
+ flags |= TI_SHORT_TRACK;
+ if (noshorttrack)
+ flags &= ~TI_SHORT_TRACK;
+ if (bswab)
+ flags |= TI_SWAB;
+ if (ispacket) {
+ flags |= TI_PACKET;
+ trackp[0].flags &= ~TI_TAO;
+ }
+ if (noclose)
+ flags |= TI_NOCLOSE;
+ if (useinfo)
+ flags |= TI_USEINFO;
+
+ if (ga_ret == NOARGS) {
+ /*
+ * All options have already been parsed and no more
+ * file type arguments are present.
+ */
+ break;
+ }
+ if (tracks == 0 && (wm == 0)) {
+ errmsgno(EX_BAD, "No write mode specified.\n");
+ errmsgno(EX_BAD, "Assuming -tao mode.\n");
+ errmsgno(EX_BAD, "Future versions of wodim may have different drive dependent defaults.\n");
+ tao = 1;
+ }
+ tracks++;
+
+ if (tracks > MAX_TRACK)
+ comerrno(EX_BAD, "Track limit (%d) exceeded\n",
+ MAX_TRACK);
+ /*
+ * Make 'tracks' immediately usable in track structure.
+ */
+ { register int i;
+ for (i = 0; i < MAX_TRACK+2; i++)
+ trackp[i].tracks = tracks;
+ }
+
+ if (strcmp("-", cav[0]) == 0)
+ *flagsp |= F_STDIN;
+
+ if (!is_auname(cav[0]) && !is_wavname(cav[0]))
+ flags |= TI_NOAUHDR;
+
+ if ((*flagsp & (F_SAO|F_RAW)) != 0 && (flags & TI_AUDIO) != 0)
+ flags |= TI_PREGAP; /* Hack for now */
+ if (tracks == 1)
+ flags &= ~TI_PREGAP;
+
+ if (tracks == 1 && (pregapsize != -1L && pregapsize != 150))
+ pregapsize = -1L;
+ trackp[tracks].filename = cav[0];
+ trackp[tracks].trackstart = 0L;
+ trackp[tracks].itracksize = tracksize;
+ trackp[tracks].tracksize = tracksize;
+ trackp[tracks].tracksecs = -1L;
+ if (tracksize >= 0) {
+ trackp[tracks].tracksecs = (tracksize+secsize-1)/secsize;
+ if(tracksize<616448)
+ warn_minisize=tracksize;
+ }
+ if (trackp[tracks].pregapsize < 0)
+ trackp[tracks].pregapsize = pregapsize;
+ trackp[tracks+1].pregapsize = -1;
+ trackp[tracks].padsecs = (padsize+2047)/2048;
+ trackp[tracks].isecsize = secsize;
+ trackp[tracks].secsize = secsize;
+ trackp[tracks].flags = flags;
+ /*
+ * XXX Dies ist falsch: auch bei SAO/RAW kann
+ * XXX secsize != isecsize sein.
+ */
+ if ((*flagsp & F_RAW) != 0) {
+ if (is_raw16(&trackp[tracks]))
+ trackp[tracks].secsize = RAW16_SEC_SIZE;
+ else
+ trackp[tracks].secsize = RAW96_SEC_SIZE;
+#ifndef HAVE_LIB_EDC_ECC
+ if ((sectype & ST_MODE_MASK) != ST_MODE_AUDIO) {
+ errmsgno(EX_BAD,
+ "EDC/ECC library not compiled in.\n");
+ comerrno(EX_BAD,
+ "Data sectors are not supported in RAW mode.\n");
+ }
+#endif
+ }
+ trackp[tracks].secspt = 0; /* transfer size is set up in set_trsizes() */
+ trackp[tracks].pktsize = pktsize;
+ trackp[tracks].trackno = tracks;
+ trackp[tracks].sectype = sectype;
+#ifdef CLONE_WRITE
+ if ((*flagsp & F_CLONE) != 0) {
+ trackp[tracks].isecsize = 2448;
+ trackp[tracks].sectype |= ST_MODE_RAW;
+ dataoff = 0;
+ }
+#endif
+ trackp[tracks].dataoff = dataoff;
+ trackp[tracks].tracktype = tracktype;
+ trackp[tracks].dbtype = dbtype;
+ trackp[tracks].flags = flags;
+ trackp[tracks].nindex = 1;
+ trackp[tracks].tindex = 0;
+ if (isrc) {
+#ifdef AUINFO
+ setisrc(isrc, &trackp[tracks]);
+#else
+ trackp[tracks].isrc = malloc(16);
+ fillbytes(trackp[tracks].isrc, 16, '\0');
+ strncpy(trackp[tracks].isrc, isrc, 12);
+#endif
+ }
+ if (tindex) {
+#ifdef AUINFO
+ setindex(tindex, &trackp[tracks]);
+#endif
+ }
+ }
+
+ if (dminbuf >= 0) {
+ if (dminbuf < 25 || dminbuf > 95)
+ comerrno(EX_BAD,
+ "Bad minbuf=%d option (must be between 25 and 95)\n",
+ dminbuf);
+ }
+
+ if (speed < 0 && speed != -1)
+ comerrno(EX_BAD, "Bad speed option.\n");
+
+ if (fs < 0L && fs != -1L)
+ comerrno(EX_BAD, "Bad fifo size option.\n");
+
+ if (bufsize < 0L && bufsize != -1L)
+ comerrno(EX_BAD, "Bad transfer size option.\n");
+ if (bufsize < 0L)
+ bufsize = CDR_BUF_SIZE;
+ if (bufsize > CDR_MAX_BUF_SIZE)
+ bufsize = CDR_MAX_BUF_SIZE;
+
+ dev = *devp;
+ cdr_defaults(&dev, &speed, &fs, &driveropts);
+ if (debug) {
+ printf("dev: '%s' speed: %d fs: %ld driveropts '%s'\n",
+ dev, speed, fs, driveropts);
+ }
+ if (speed >= 0)
+ *speedp = speed;
+
+ if (fs < 0L)
+ fs = DEFAULT_FIFOSIZE;
+ if (fs < 2*bufsize) {
+ errmsgno(EX_BAD, "Fifo size %ld too small, turning fifo off.\n", fs);
+ fs = 0L;
+ }
+
+ if (dev != *devp && (*flagsp & F_SCANBUS) == 0)
+ *devp = dev;
+
+ if (*devp &&
+ ((strncmp(*devp, "HELP", 4) == 0) ||
+ (strncmp(*devp, "help", 4) == 0))) {
+ *flagsp |= F_CHECKDRIVE; /* Set this for not calling mlockall() */
+ return ispacket;
+ }
+ if (*flagsp & (F_LOAD|F_DLCK|F_SETDROPTS|F_MSINFO|F_TOC|F_PRATIP|F_FIX|F_VERSION|F_CHECKDRIVE|F_PRCAP|F_INQUIRY|F_SCANBUS|F_RESET|F_ABORT)) {
+ if (tracks != 0) {
+ fprintf(stderr,
+ "No tracks allowed with -load, -lock, -setdropts, -msinfo, -toc, -atip, -fix,\n"
+ "-version, -checkdrive, -prcap, -inq, -scanbus, --devices, -reset and -abort options.\n" );
+ exit(EXIT_FAILURE);
+ }
+ return ispacket;
+ }
+ *tracksp = tracks;
+ if (*flagsp & F_SAO) {
+ /*
+ * Make sure that you change WRITER_MAXWAIT & READER_MAXWAIT
+ * too if you change this timeout.
+ */
+ if (*timeoutp < 200) /* Lead in size is 2:30 */
+ *timeoutp = 200; /* 200s is 150s *1.33 */
+ }
+ if (usetext) {
+ trackp[MAX_TRACK+1].flags |= TI_TEXT;
+ }
+ if (cuefile) {
+#ifdef FUTURE
+ if ((*flagsp & F_SAO) == 0 &&
+ (*flagsp & F_RAW) == 0) {
+#else
+ if ((*flagsp & F_SAO) == 0) {
+#endif
+ errmsgno(EX_BAD, "The cuefile= option only works with -dao.\n");
+ susage(EX_BAD);
+ }
+ if (tracks > 0) {
+ errmsgno(EX_BAD, "No tracks allowed with the cuefile= option\n");
+ susage(EX_BAD);
+ }
+ cuefilename = cuefile;
+ return ispacket;
+ }
+ if (tracks == 0 && (*flagsp & (F_LOAD|F_DLCK|F_EJECT|F_BLANK|F_FORMAT)) == 0) {
+ errmsgno(EX_BAD, "No tracks specified. Need at least one.\n");
+ susage(EX_BAD);
+ }
+ return ispacket;
+}
+
+static void
+set_trsizes(cdr_t *dp, int tracks, track_t *trackp)
+{
+ int i;
+ int secsize;
+ int secspt;
+
+ trackp[1].flags |= TI_FIRST;
+ trackp[tracks].flags |= TI_LAST;
+
+ if (xdebug)
+ printf("Set Transfersizes start\n");
+ for (i = 0; i <= tracks+1; i++) {
+ if ((dp->cdr_flags & CDR_SWABAUDIO) != 0 &&
+ is_audio(&trackp[i])) {
+ trackp[i].flags ^= TI_SWAB;
+ }
+ if (!is_audio(&trackp[i]))
+ trackp[i].flags &= ~TI_SWAB; /* Only swab audio */
+
+ /*
+ * Use the biggest sector size to compute how many
+ * sectors may fit into one single DMA buffer.
+ */
+ secsize = trackp[i].secsize;
+ if (trackp[i].isecsize > secsize)
+ secsize = trackp[i].isecsize;
+
+ /*
+ * We are using SCSI Group 0 write
+ * and cannot write more than 255 secs at once.
+ */
+ secspt = bufsize/secsize;
+ secspt = min(255, secspt);
+ trackp[i].secspt = secspt;
+
+ if (is_packet(&trackp[i]) && trackp[i].pktsize > 0) {
+ if (trackp[i].secspt >= trackp[i].pktsize) {
+ trackp[i].secspt = trackp[i].pktsize;
+ } else {
+ comerrno(EX_BAD,
+ "Track %d packet size %d exceeds buffer limit of %d sectors",
+ i, trackp[i].pktsize, trackp[i].secspt);
+ }
+ }
+ if (xdebug) {
+ printf("Track %d flags %X secspt %d secsize: %d isecsize: %d\n",
+ i, trackp[i].flags, trackp[i].secspt,
+ trackp[i].secsize, trackp[i].isecsize);
+ }
+ }
+ if (xdebug)
+ printf("Set Transfersizes end\n");
+}
+
+void
+load_media(SCSI *usalp, cdr_t *dp, BOOL doexit)
+{
+ int code;
+ int key;
+ BOOL immed = (dp->cdr_cmdflags&F_IMMED) != 0;
+
+ /*
+ * Do some preparation before...
+ */
+ usalp->silent++; /* Be quiet if this fails */
+ test_unit_ready(usalp); /* First eat up unit attention */
+ if ((*dp->cdr_load)(usalp, dp) < 0) { /* now try to load media and */
+ if (!doexit)
+ return;
+ comerrno(EX_BAD, "Cannot load media.\n");
+ }
+ scsi_start_stop_unit(usalp, 1, 0, immed); /* start unit in silent mode */
+ usalp->silent--;
+
+ if (!wait_unit_ready(usalp, 60)) {
+ code = usal_sense_code(usalp);
+ key = usal_sense_key(usalp);
+ usalp->silent++;
+ scsi_prevent_removal(usalp, 0); /* In case someone locked it */
+ usalp->silent--;
+
+ if (!doexit)
+ return;
+ if (key == SC_NOT_READY && (code == 0x3A || code == 0x30))
+ comerrno(EX_BAD, "No disk / Wrong disk!\n");
+ comerrno(EX_BAD, "CD/DVD-Recorder not ready.\n");
+ }
+
+ scsi_prevent_removal(usalp, 1);
+ scsi_start_stop_unit(usalp, 1, 0, immed);
+ wait_unit_ready(usalp, 120);
+ usalp->silent++;
+ if(geteuid() == 0) /* EB: needed? Not allowed for non-root, that is sure. */
+ rezero_unit(usalp); /* Is this needed? Not supported by some drvives */
+ usalp->silent--;
+ test_unit_ready(usalp);
+ scsi_start_stop_unit(usalp, 1, 0, immed);
+ wait_unit_ready(usalp, 120);
+}
+
+void
+unload_media(SCSI *usalp, cdr_t *dp, int flags)
+{
+ scsi_prevent_removal(usalp, 0);
+ if ((flags & F_EJECT) != 0) {
+ if ((*dp->cdr_unload)(usalp, dp) < 0)
+ errmsgno(EX_BAD, "Cannot eject media.\n");
+ }
+}
+
+void
+reload_media(SCSI *usalp, cdr_t *dp)
+{
+ char ans[2];
+#ifdef F_GETFL
+ int f = -1;
+#endif
+
+ errmsgno(EX_BAD, "Drive needs to reload the media to return to proper status.\n");
+ unload_media(usalp, dp, F_EJECT);
+
+ /*
+ * Note that even Notebook drives identify as CDR_TRAYLOAD
+ */
+ if ((dp->cdr_flags & CDR_TRAYLOAD) != 0) {
+ usalp->silent++;
+ load_media(usalp, dp, FALSE);
+ usalp->silent--;
+ }
+
+ usalp->silent++;
+ if (((dp->cdr_flags & CDR_TRAYLOAD) == 0) ||
+ !wait_unit_ready(usalp, 5)) {
+ static FILE *tty = NULL;
+
+ printf("Re-load disk and hit <CR>");
+ if (isgui)
+ printf("\n");
+ flush();
+
+ if (tty == NULL) {
+ tty = stdin;
+ if ((dp->cdr_cmdflags & F_STDIN) != 0)
+ tty = fileluopen(STDERR_FILENO, "rw");
+ }
+#ifdef F_GETFL
+ if (tty != NULL)
+ f = fcntl(fileno(tty), F_GETFL, 0);
+ if (f < 0 || (f & O_ACCMODE) == O_WRONLY) {
+#ifdef SIGUSR1
+ signal(SIGUSR1, catchsig);
+ printf("Controlling file not open for reading, send SIGUSR1 to continue.\n");
+ flush();
+ pause();
+#endif
+ } else
+#endif
+ if (rols_fgetline(tty, ans, 1) < 0)
+ comerrno(EX_BAD, "Aborted by EOF on input.\n");
+ }
+ usalp->silent--;
+
+ load_media(usalp, dp, TRUE);
+}
+
+void
+set_secsize(SCSI *usalp, int secsize)
+{
+ if (secsize > 0) {
+ /*
+ * Try to restore the old sector size.
+ */
+ usalp->silent++;
+ select_secsize(usalp, secsize);
+ usalp->silent--;
+ }
+}
+
+static int
+get_dmaspeed(SCSI *usalp, cdr_t *dp)
+{
+ int i;
+ long t;
+ int bs;
+ int tsize;
+
+ if(getenv("CDR_NODMATEST"))
+ return -1;
+
+ if (debug || lverbose)
+ fprintf( stderr,
+ "Beginning DMA speed test. Set CDR_NODMATEST environment variable if device\n"
+ "communication breaks or freezes immediately after that.\n" );
+
+ fillbytes((caddr_t)buf, 4, '\0');
+ tsize = 0;
+ usalp->silent++;
+ i = read_buffer(usalp, buf, 4, 0);
+ usalp->silent--;
+ if (i < 0)
+ return (-1);
+ tsize = a_to_u_4_byte(buf);
+ if (tsize <= 0)
+ return (-1);
+
+ if (gettimeofday(&starttime, (struct timezone *)0) < 0)
+ return (-1);
+
+ bs = bufsize;
+ if (tsize < bs)
+ bs = tsize;
+ for (i = 0; i < 100; i++) {
+ if (read_buffer(usalp, buf, bs, 0) < 0)
+ return (-1);
+ }
+ if (gettimeofday(&fixtime, (struct timezone *)0) < 0) {
+ errmsg("Cannot get DMA stop time\n");
+ return (-1);
+ }
+ timevaldiff(&starttime, &fixtime);
+ tsize = bs * 100;
+ t = fixtime.tv_sec * 1000 + fixtime.tv_usec / 1000;
+ if (t <= 0)
+ return (-1);
+#ifdef DEBUG
+ fprintf(stderr, "Read Speed: %lu %ld %ld kB/s %ldx CD %ldx DVD\n",
+ tsize, t, tsize/t, tsize/t/176, tsize/t/1385);
+#endif
+
+ return (tsize/t);
+}
+
+
+static BOOL
+do_opc(SCSI *usalp, cdr_t *dp, int flags)
+{
+ if ((flags & F_DUMMY) == 0 && dp->cdr_opc) {
+ if (debug || lverbose) {
+ printf("Performing OPC...\n");
+ flush();
+ }
+ if (dp->cdr_opc(usalp, NULL, 0, TRUE) < 0) {
+ errmsgno(EX_BAD, "OPC failed.\n");
+ if ((flags & F_FORCE) == 0)
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
+
+static void
+check_recovery(SCSI *usalp, cdr_t *dp, int flags)
+{
+ if ((*dp->cdr_check_recovery)(usalp, dp)) {
+ errmsgno(EX_BAD, "Recovery needed.\n");
+ unload_media(usalp, dp, flags);
+ comexit(EX_BAD);
+ }
+}
+
+#ifndef DEBUG
+#define DEBUG
+#endif
+void
+audioread(SCSI *usalp, cdr_t *dp, int flags)
+{
+#ifdef DEBUG
+ int speed = 1;
+ int oflags = dp->cdr_cmdflags;
+
+ dp->cdr_cmdflags &= ~F_DUMMY;
+ if ((*dp->cdr_set_speed_dummy)(usalp, dp, &speed) < 0)
+ comexit(-1);
+ dp->cdr_dstat->ds_wspeed = speed; /* XXX Remove 'speed' in future */
+ dp->cdr_cmdflags = oflags;
+
+ if ((*dp->cdr_set_secsize)(usalp, 2352) < 0)
+ comexit(-1);
+ usalp->cap->c_bsize = 2352;
+
+ read_scsi(usalp, buf, 1000, 1);
+ printf("XXX:\n");
+ write(1, buf, 512); /* FIXME: handle return value */
+ unload_media(usalp, dp, flags);
+ comexit(0);
+#endif
+}
+
+static void
+print_msinfo(SCSI *usalp, cdr_t *dp)
+{
+ long off;
+ long fa;
+
+ if ((*dp->cdr_session_offset)(usalp, &off) < 0) {
+ errmsgno(EX_BAD, "Cannot read session offset\n");
+ return;
+ }
+ if (lverbose)
+ printf("session offset: %ld\n", off);
+
+ if (dp->cdr_next_wr_address(usalp, (track_t *)0, &fa) < 0) {
+ errmsgno(EX_BAD, "Cannot read first writable address\n");
+ return;
+ }
+ printf("%ld,%ld\n", off, fa);
+ if(msifile) {
+ FILE *f = fopen(msifile, "w");
+ if(f) {
+ fprintf(f, "%ld,%ld", off, fa);
+ fclose(f);
+ }
+ else {
+ perror("Unable to write multi session info file");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+static void
+print_toc(SCSI *usalp, cdr_t *dp)
+{
+ int first;
+ int last;
+ long lba;
+ long xlba;
+ struct msf msf;
+ int adr;
+ int control;
+ int mode;
+ int i;
+
+ usalp->silent++;
+ if (read_capacity(usalp) < 0) {
+ usalp->silent--;
+ errmsgno(EX_BAD, "Cannot read capacity\n");
+ return;
+ }
+ usalp->silent--;
+ if (read_tochdr(usalp, dp, &first, &last) < 0) {
+ errmsgno(EX_BAD, "Cannot read TOC/PMA\n");
+ return;
+ }
+ printf("first: %d last %d\n", first, last);
+ for (i = first; i <= last; i++) {
+ read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode);
+ xlba = -150 +
+ msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min);
+ if (xlba == lba/4)
+ lba = xlba;
+ print_track(i, lba, &msf, adr, control, mode);
+ }
+ i = 0xAA;
+ read_trackinfo(usalp, i, &lba, &msf, &adr, &control, &mode);
+ xlba = -150 +
+ msf.msf_frame + (75*msf.msf_sec) + (75*60*msf.msf_min);
+ if (xlba == lba/4)
+ lba = xlba;
+ print_track(i, lba, &msf, adr, control, mode);
+ if (lverbose > 1) {
+ usalp->silent++;
+ if (read_cdtext(usalp) < 0)
+ errmsgno(EX_BAD, "No CD-Text or CD-Text unaware drive.\n");
+ usalp->silent++;
+ }
+}
+
+static void
+print_track(int track, long lba, struct msf *msp, int adr,
+ int control, int mode)
+{
+ long lba_512 = lba*4;
+
+ if (track == 0xAA)
+ printf("track:lout ");
+ else
+ printf("track: %3d ", track);
+
+ printf("lba: %9ld (%9ld) %02d:%02d:%02d adr: %X control: %X mode: %d\n",
+ lba, lba_512,
+ msp->msf_min,
+ msp->msf_sec,
+ msp->msf_frame,
+ adr, control, mode);
+}
+
+#ifdef HAVE_SYS_PRIOCNTL_H /* The preferred SYSvR4 schduler */
+
+#include <sys/procset.h> /* Needed for SCO Openserver */
+#include <sys/priocntl.h>
+#include <sys/rtpriocntl.h>
+
+void
+raisepri(int pri)
+{
+ int pid;
+ int classes;
+ int ret;
+ pcinfo_t info;
+ pcparms_t param;
+ rtinfo_t rtinfo;
+ rtparms_t rtparam;
+
+ pid = getpid();
+
+ /* get info */
+ strcpy(info.pc_clname, "RT");
+ classes = priocntl(P_PID, pid, PC_GETCID, (void *)&info);
+ if (classes == -1)
+ comerr("Cannot get priority class id priocntl(PC_GETCID)\n");
+
+ movebytes(info.pc_clinfo, &rtinfo, sizeof (rtinfo_t));
+
+ /* set priority to max */
+ rtparam.rt_pri = rtinfo.rt_maxpri - pri;
+ rtparam.rt_tqsecs = 0;
+ rtparam.rt_tqnsecs = RT_TQDEF;
+ param.pc_cid = info.pc_cid;
+ movebytes(&rtparam, param.pc_clparms, sizeof (rtparms_t));
+ ret = priocntl(P_PID, pid, PC_SETPARMS, (void *)&param);
+ if (ret == -1) {
+ errmsg("WARNING: Cannot set priority class parameters priocntl(PC_SETPARMS)\n");
+ errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
+ }
+}
+
+#else /* HAVE_SYS_PRIOCNTL_H */
+
+#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING -0 >= 0
+/*
+ * The second best choice: POSIX real time scheduling.
+ */
+/*
+ * XXX Ugly but needed because of a typo in /usr/iclude/sched.h on Linux.
+ * XXX This should be removed as soon as we are sure that Linux-2.0.29 is gone.
+ */
+#ifdef __linux
+#define _P __P
+#endif
+
+#include <sched.h>
+
+#ifdef __linux
+#undef _P
+#endif
+
+static int
+rt_raisepri(int pri)
+{
+ struct sched_param scp;
+
+ /*
+ * Verify that scheduling is available
+ */
+#ifdef _SC_PRIORITY_SCHEDULING
+ if (sysconf(_SC_PRIORITY_SCHEDULING) == -1) {
+ errmsg("WARNING: RR-scheduler not available, disabling.\n");
+ return (-1);
+ }
+#endif
+ fillbytes(&scp, sizeof (scp), '\0');
+ scp.sched_priority = sched_get_priority_max(SCHED_RR) - pri;
+ if (sched_setscheduler(0, SCHED_RR, &scp) < 0) {
+ if(lverbose>2)
+ errmsg("WARNING: Cannot set RR-scheduler\n");
+ return (-1);
+ }
+ return (0);
+}
+
+#else /* _POSIX_PRIORITY_SCHEDULING */
+
+#ifdef __CYGWIN32__
+/*
+ * Win32 specific priority settings.
+ */
+/*
+ * NOTE: Base.h from Cygwin-B20 has a second typedef for BOOL.
+ * We define BOOL to make all static code use BOOL
+ * from Windows.h and use the hidden __SBOOL for
+ * our global interfaces.
+ *
+ * NOTE: windows.h from Cygwin-1.x includes a structure field named sample,
+ * so me may not define our own 'sample' or need to #undef it now.
+ * With a few nasty exceptions, Microsoft assumes that any global
+ * defines or identifiers will begin with an Uppercase letter, so
+ * there may be more of these problems in the future.
+ *
+ * NOTE: windows.h defines interface as an alias for struct, this
+ * is used by COM/OLE2, I guess it is class on C++
+ * We man need to #undef 'interface'
+ */
+#define BOOL WBOOL /* This is the Win BOOL */
+#define format __format /* Avoid format parameter hides global ... */
+#include <windows.h>
+#undef format
+#undef interface
+
+static int
+rt_raisepri(int pri)
+{
+ int prios[] = {THREAD_PRIORITY_TIME_CRITICAL, THREAD_PRIORITY_HIGHEST};
+
+ /* set priority class */
+ if (SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS) == FALSE) {
+ if(debug || lverbose>2)
+ errmsgno(EX_BAD, "No realtime priority class possible.\n");
+ return (-1);
+ }
+
+ /* set thread priority */
+ if (pri >= 0 && pri <= 1 && SetThreadPriority(GetCurrentThread(), prios[pri]) == FALSE) {
+ errmsgno(EX_BAD, "Could not set realtime priority.\n");
+ return (-1);
+ }
+ return (0);
+}
+
+#else
+/*
+ * This OS does not support real time scheduling.
+ */
+static int
+rt_raisepri(int pri)
+{
+ return (-1);
+}
+
+#endif /* __CYGWIN32__ */
+
+#endif /* _POSIX_PRIORITY_SCHEDULING */
+
+void
+raisepri(int pri)
+{
+ if (rt_raisepri(pri) >= 0)
+ return;
+#if defined(HAVE_SETPRIORITY) && defined(PRIO_PROCESS)
+
+ if (setpriority(PRIO_PROCESS, getpid(), -20 + pri) < 0) {
+ if(debug || lverbose>2)
+ fprintf(stderr,
+ "WARNING: Cannot set priority using setpriority(),"
+ "increased risk for buffer underruns.\n");
+ }
+#else
+#ifdef HAVE_DOSSETPRIORITY /* RT priority on OS/2 */
+ /*
+ * Set priority to timecritical 31 - pri (arg)
+ */
+ DosSetPriority(0, 3, 31, 0);
+ DosSetPriority(0, 3, -pri, 0);
+#else
+#if defined(HAVE_NICE) && !defined(__DJGPP__) /* DOS has nice but no multitasking */
+ if (nice(-20 + pri) == -1) {
+ errmsg("WARNING: Cannot set priority using nice().\n");
+ errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
+ }
+#else
+ errmsgno(EX_BAD, "WARNING: Cannot set priority on this OS.\n");
+ errmsgno(EX_BAD, "WARNING: This causes a high risk for buffer underruns.\n");
+#endif
+#endif
+#endif
+}
+
+#endif /* HAVE_SYS_PRIOCNTL_H */
+
+#ifdef HAVE_SELECT
+/*
+ * sys/types.h and sys/time.h are already included.
+ */
+#else
+# include <stropts.h>
+# include <poll.h>
+
+#ifndef INFTIM
+#define INFTIM (-1)
+#endif
+#endif
+
+#if defined(HAVE_SELECT) && defined(NEED_SYS_SELECT_H)
+#include <sys/select.h>
+#endif
+#if defined(HAVE_SELECT) && defined(NEED_SYS_SOCKET_H)
+#include <sys/socket.h>
+#endif
+
+static void
+wait_input()
+{
+#ifdef HAVE_SELECT
+ fd_set in;
+
+ FD_ZERO(&in);
+ FD_SET(STDIN_FILENO, &in);
+ select(1, &in, NULL, NULL, 0);
+#else
+ struct pollfd pfd;
+
+ pfd.fd = STDIN_FILENO;
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+ poll(&pfd, (unsigned long)1, INFTIM);
+#endif
+}
+
+static void
+checkgui()
+{
+ struct stat st;
+
+ if (fstat(STDERR_FILENO, &st) >= 0 && !S_ISCHR(st.st_mode)) {
+ isgui = TRUE;
+ if (lverbose > 1)
+ printf("Using remote (pipe) mode for interactive i/o.\n");
+ }
+}
+
+static int
+getbltype(char *optstr, long *typep)
+{
+ if (streql(optstr, "all")) {
+ *typep = BLANK_DISC;
+ } else if (streql(optstr, "disc")) {
+ *typep = BLANK_DISC;
+ } else if (streql(optstr, "disk")) {
+ *typep = BLANK_DISC;
+ } else if (streql(optstr, "fast")) {
+ *typep = BLANK_MINIMAL;
+ } else if (streql(optstr, "minimal")) {
+ *typep = BLANK_MINIMAL;
+ } else if (streql(optstr, "track")) {
+ *typep = BLANK_TRACK;
+ } else if (streql(optstr, "unreserve")) {
+ *typep = BLANK_UNRESERVE;
+ } else if (streql(optstr, "trtail")) {
+ *typep = BLANK_TAIL;
+ } else if (streql(optstr, "unclose")) {
+ *typep = BLANK_UNCLOSE;
+ } else if (streql(optstr, "session")) {
+ *typep = BLANK_SESSION;
+ } else if (streql(optstr, "help")) {
+ blusage(0);
+ } else {
+ fprintf(stderr, "Illegal blanking type '%s'.\n", optstr);
+ blusage(EX_BAD);
+ return (-1);
+ }
+ return (TRUE);
+}
+
+static int
+getformattype(char *optstr, long *typep)
+{
+ if (streql(optstr, "full")) {
+ *typep = FULL_FORMAT;
+ } else if (streql(optstr, "background")) {
+ *typep = BACKGROUND_FORMAT;
+ } else if (streql(optstr, "force")) {
+ *typep = FORCE_FORMAT;
+ } else if (streql(optstr, "help")) {
+ formattypeusage(0);
+ } else {
+ fprintf(stderr, "Illegal blanking type '%s'.\n", optstr);
+ formattypeusage(EX_BAD);
+ return (-1);
+ }
+ return (TRUE);
+}
+static void
+print_drflags(cdr_t *dp)
+{
+ printf("Driver flags : ");
+
+ if ((dp->cdr_flags & CDR_DVD) != 0)
+ printf("DVD ");
+
+ if ((dp->cdr_flags & CDR_MMC3) != 0)
+ printf("MMC-3 ");
+ else if ((dp->cdr_flags & CDR_MMC2) != 0)
+ printf("MMC-2 ");
+ else if ((dp->cdr_flags & CDR_MMC) != 0)
+ printf("MMC ");
+
+ if ((dp->cdr_flags & CDR_SWABAUDIO) != 0)
+ printf("SWABAUDIO ");
+ if ((dp->cdr_flags & CDR_BURNFREE) != 0)
+ printf("BURNFREE ");
+ if ((dp->cdr_flags & CDR_VARIREC) != 0)
+ printf("VARIREC ");
+ if ((dp->cdr_flags & CDR_GIGAREC) != 0)
+ printf("GIGAREC ");
+ if ((dp->cdr_flags & CDR_AUDIOMASTER) != 0)
+ printf("AUDIOMASTER ");
+ if ((dp->cdr_flags & CDR_FORCESPEED) != 0)
+ printf("FORCESPEED ");
+ if ((dp->cdr_flags & CDR_SPEEDREAD) != 0)
+ printf("SPEEDREAD ");
+ if ((dp->cdr_flags & CDR_DISKTATTOO) != 0)
+ printf("DISKTATTOO ");
+ if ((dp->cdr_flags & CDR_SINGLESESS) != 0)
+ printf("SINGLESESSION ");
+ if ((dp->cdr_flags & CDR_HIDE_CDR) != 0)
+ printf("HIDECDR ");
+ printf("\n");
+}
+
+static void
+print_wrmodes(cdr_t *dp)
+{
+ BOOL needblank = FALSE;
+
+ printf("Supported modes: ");
+ if ((dp->cdr_flags & CDR_TAO) != 0) {
+ printf("TAO");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & CDR_PACKET) != 0) {
+ printf("%sPACKET", needblank?" ":"");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & CDR_SAO) != 0) {
+ printf("%sSAO", needblank?" ":"");
+ needblank = TRUE;
+ }
+#ifdef __needed__
+ if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW16)) == (CDR_SAO|CDR_SRAW16)) {
+ printf("%sSAO/R16", needblank?" ":"");
+ needblank = TRUE;
+ }
+#endif
+ if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96P)) == (CDR_SAO|CDR_SRAW96P)) {
+ printf("%sSAO/R96P", needblank?" ":"");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & (CDR_SAO|CDR_SRAW96R)) == (CDR_SAO|CDR_SRAW96R)) {
+ printf("%sSAO/R96R", needblank?" ":"");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & (CDR_RAW|CDR_RAW16)) == (CDR_RAW|CDR_RAW16)) {
+ printf("%sRAW/R16", needblank?" ":"");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96P)) == (CDR_RAW|CDR_RAW96P)) {
+ printf("%sRAW/R96P", needblank?" ":"");
+ needblank = TRUE;
+ }
+ if ((dp->cdr_flags & (CDR_RAW|CDR_RAW96R)) == (CDR_RAW|CDR_RAW96R)) {
+ printf("%sRAW/R96R", needblank?" ":"");
+ needblank = TRUE;
+ }
+ printf("\n");
+}
+
+static BOOL
+check_wrmode(cdr_t *dp, int wmode, int tflags)
+{
+ int cdflags = dp->cdr_flags;
+
+ if ((tflags & TI_PACKET) != 0 && (cdflags & CDR_PACKET) == 0) {
+ errmsgno(EX_BAD, "Drive does not support PACKET recording.\n");
+ return (FALSE);
+ }
+ if ((tflags & TI_TAO) != 0 && (cdflags & CDR_TAO) == 0) {
+ errmsgno(EX_BAD, "Drive does not support TAO recording.\n");
+ return (FALSE);
+ }
+ if ((wmode & F_SAO) != 0) {
+ if ((cdflags & CDR_SAO) == 0) {
+ errmsgno(EX_BAD, "Drive does not support SAO recording.\n");
+ if ((cdflags & CDR_RAW) != 0)
+ errmsgno(EX_BAD, "Try -raw option.\n");
+ return (FALSE);
+ }
+#ifdef __needed__
+ if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_SRAW16) == 0) {
+ errmsgno(EX_BAD, "Drive does not support SAO/RAW16.\n");
+ goto badsecs;
+ }
+#endif
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_SRAW96P) == 0) {
+ errmsgno(EX_BAD, "Drive does not support SAO/RAW96P.\n");
+ goto badsecs;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_SRAW96R) == 0) {
+ errmsgno(EX_BAD, "Drive does not support SAO/RAW96R.\n");
+ goto badsecs;
+ }
+ }
+ if ((wmode & F_RAW) != 0) {
+ if ((cdflags & CDR_RAW) == 0) {
+ errmsgno(EX_BAD, "Drive does not support RAW recording.\n");
+ return (FALSE);
+ }
+ if ((tflags & TI_RAW16) != 0 && (cdflags & CDR_RAW16) == 0) {
+ errmsgno(EX_BAD, "Drive does not support RAW/RAW16.\n");
+ goto badsecs;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW && (cdflags & CDR_RAW96P) == 0) {
+ errmsgno(EX_BAD, "Drive does not support RAW/RAW96P.\n");
+ goto badsecs;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R) && (cdflags & CDR_RAW96R) == 0) {
+ errmsgno(EX_BAD, "Drive does not support RAW/RAW96R.\n");
+ goto badsecs;
+ }
+ }
+ return (TRUE);
+
+badsecs:
+ if ((wmode & F_SAO) != 0)
+ cdflags &= ~(CDR_RAW16|CDR_RAW96P|CDR_RAW96R);
+ if ((wmode & F_RAW) != 0)
+ cdflags &= ~(CDR_SRAW96P|CDR_SRAW96R);
+
+ if ((cdflags & (CDR_SRAW96R|CDR_RAW96R)) != 0)
+ errmsgno(EX_BAD, "Try -raw96r option.\n");
+ else if ((cdflags & (CDR_SRAW96P|CDR_RAW96P)) != 0)
+ errmsgno(EX_BAD, "Try -raw96p option.\n");
+ else if ((cdflags & CDR_RAW16) != 0)
+ errmsgno(EX_BAD, "Try -raw16 option.\n");
+ return (FALSE);
+}
+
+static void
+set_wrmode(cdr_t *dp, int wmode, int tflags)
+{
+ dstat_t *dsp = dp->cdr_dstat;
+
+ if ((tflags & TI_PACKET) != 0) {
+ dsp->ds_wrmode = WM_PACKET;
+ return;
+ }
+ if ((tflags & TI_TAO) != 0) {
+ dsp->ds_wrmode = WM_TAO;
+ return;
+ }
+ if ((wmode & F_SAO) != 0) {
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == 0) {
+ dsp->ds_wrmode = WM_SAO;
+ return;
+ }
+ if ((tflags & TI_RAW16) != 0) { /* Is this needed? */
+ dsp->ds_wrmode = WM_SAO_RAW16;
+ return;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) {
+ dsp->ds_wrmode = WM_SAO_RAW96P;
+ return;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) {
+ dsp->ds_wrmode = WM_SAO_RAW96R;
+ return;
+ }
+ }
+ if ((wmode & F_RAW) != 0) {
+ if ((tflags & TI_RAW16) != 0) {
+ dsp->ds_wrmode = WM_RAW_RAW16;
+ return;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW) {
+ dsp->ds_wrmode = WM_RAW_RAW96P;
+ return;
+ }
+ if ((tflags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R)) {
+ dsp->ds_wrmode = WM_RAW_RAW96R;
+ return;
+ }
+ }
+ dsp->ds_wrmode = WM_NONE;
+}
+
+#if defined(linux) || defined(__linux) || defined(__linux__)
+#ifdef HAVE_UNAME
+#include <sys/utsname.h>
+#endif
+#endif
+
+#ifdef __linux__
+static int
+get_cap(cap_value_t cap_array)
+{
+ int ret;
+ cap_t capa;
+ capa = cap_get_proc();
+ cap_set_flag(capa, CAP_EFFECTIVE, 1, &cap_array, CAP_SET);
+ ret = cap_set_proc(capa);
+ cap_free(capa);
+ return ret;
+}
+#endif
diff --git a/wodim/wodim.dfl b/wodim/wodim.dfl
new file mode 100644
index 0000000..41c4201
--- /dev/null
+++ b/wodim/wodim.dfl
@@ -0,0 +1,48 @@
+# wodim.dfl Copyright 2006 E. Bloch
+# Based on cdrecord.dfl (Copyright 1998 J. Schilling)
+#
+# This file is /etc/wodim.conf
+# It contains defaults that are used if no command line option
+# or environment is present.
+#
+# The default device, if not specified elsewhere.
+#
+#CDR_DEVICE=yamaha
+CDR_DEVICE=cdrom
+
+#
+# The default speed, if not specified elswhere.
+#
+# For MMC compliant drives, the default is to write at maximum speed, so it in
+# general does not make sense to set up a default speed in /etc/wodim.conf.
+#
+#CDR_SPEED=40
+
+#
+# The default FIFO size if, not specified elswhere.
+#
+CDR_FIFOSIZE=12m
+
+#
+# CDR_MAXFIFOSIZE can limit the maximum allowed FIFO size. This is useful to
+# not let mallicious users allocate too much system memory if no ulimit is set
+# or wodim runs with suid-root permissions.
+#
+# CDR_MAXFIFOSIZE=256m
+
+#
+# The following definitions allow abstract device names. They are used if the
+# device name does not contain the the characters ',', ':', '/' and '@'
+#
+# Unless you have a good reason, use speed == -1 and let wodim use its internal
+# drive specific defaults.
+#
+# drive name device speed fifosize driveropts
+#
+#default= USCSI:1,0,0 -1 -1 burnfree
+#sanyo= 1,4,0 -1 -1 burnfree
+#cdrom= 0,6,0 2 1m ""
+#remote= REMOTE:rscsi@somehost:1,0,0 16 32m burnfree
+#
+cdrom= -1 -1 -1 burnfree
+
diff --git a/wodim/wodim.h b/wodim/wodim.h
new file mode 100644
index 0000000..0dfdfdc
--- /dev/null
+++ b/wodim/wodim.h
@@ -0,0 +1,1212 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)cdrecord.h 1.165 05/06/11 Copyright 1995-2005 J. Schilling */
+/*
+ * Definitions for cdrecord
+ *
+ * Copyright (c) 1995-2005 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * Make sure it is there. We need it for off_t.
+ */
+#ifndef _INCL_SYS_TYPES_H
+#include <sys/types.h>
+#define _INCL_SYS_TYPES_H
+#endif
+
+#ifndef _UTYPES_H
+#include <utypes.h>
+#endif
+
+/*
+ * Defines for command line option flags
+ */
+#define F_DUMMY 0x00000001L /* Do dummy (simulation) writes */
+#define F_TOC 0x00000002L /* Get TOC */
+#define F_EJECT 0x00000004L /* Eject disk after doing the work */
+#define F_LOAD 0x00000008L /* Load disk only */
+#define F_MULTI 0x00000010L /* Create linkable TOC/multi-session */
+#define F_MSINFO 0x00000020L /* Get multi-session info */
+#define F_FIX 0x00000040L /* Fixate disk only */
+#define F_NOFIX 0x00000080L /* Do not fixate disk */
+#define F_VERSION 0x00000100L /* Print version info */
+#define F_CHECKDRIVE 0x00000200L /* Check for driver */
+#define F_INQUIRY 0x00000400L /* Do inquiry */
+#define F_PRCAP 0x00000800L /* Print capabilities */
+#define F_SCANBUS 0x00001000L /* Scan SCSI Bus */
+#define F_RESET 0x00002000L /* Reset SCSI Bus */
+#define F_BLANK 0x00004000L /* Blank CD-RW */
+#define F_PRATIP 0x00008000L /* Print ATIP info */
+#define F_PRDINFO 0x00010000L /* Print disk info (XXX not yet used) */
+#define F_IGNSIZE 0x00020000L /* Ignore disk size */
+#define F_SAO 0x00040000L /* Session at once */
+#define F_RAW 0x00080000L /* Raw mode */
+#define F_WRITE 0x00100000L /* Disk is going to be written */
+#define F_FORCE 0x00200000L /* Force things (e.g. blank on dead disk) */
+#define F_WAITI 0x00400000L /* Wait until data is available on stdin */
+#define F_OVERBURN 0x00800000L /* Allow oveburning */
+#define F_CLONE 0x01000000L /* Do clone writing */
+#define F_STDIN 0x02000000L /* We are using stdin as CD data */
+#define F_IMMED 0x04000000L /* Try tu use IMMED SCSI flag if possible */
+#define F_DLCK 0x08000000L /* Load disk and lock door */
+#define F_SETDROPTS 0x10000000L /* Set driver opts and exit */
+#define F_FORMAT 0x20000000L /* Format media */
+#define F_ABORT 0x40000000L /* Send abort sequence to drive */
+
+#ifdef min
+#undef min
+#endif
+#define min(a, b) ((a) < (b) ? (a):(b))
+
+#ifdef max
+#undef max
+#endif
+#define max(a, b) ((a) < (b) ? (b):(a))
+
+#undef roundup
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/*
+ * NOTICE: You should not make CDR_BUF_SIZE more than
+ * the buffer size of the CD-Recorder.
+ *
+ * The Philips CDD 521 is the recorder with the smallest buffer.
+ * It only has 256kB of buffer RAM.
+ *
+ * WARNING: Philips CDD 521 dies if CDR_BUF_SIZE is to big.
+ * If you like to support the CDD 521 keep the buffer
+ * at 63kB.
+ */
+/*#define CDR_BUF_SIZE (126*1024)*/
+#define CDR_BUF_SIZE (63*1024)
+#define CDR_MAX_BUF_SIZE (256*1024)
+
+/*
+ * Never set MIN_GRACE_TIME < 3 seconds. We need to give
+ * the volume management a chance to settle before we
+ * start writing.
+ */
+#ifndef MIN_GRACE_TIME
+#define MIN_GRACE_TIME 0 /* changed to 0, there is no point in forcing it in this application. Fix your volume management if it breaks because it could not read the medium. */
+#endif
+#ifndef GRACE_TIME
+#define GRACE_TIME 4
+#endif
+
+/*
+ * Some sector sizes used for reading/writing ...
+ */
+#define DATA_SEC_SIZE 2048 /* 2048 bytes */
+#define MODE2_SEC_SIZE 2336 /* 2336 bytes */
+#define AUDIO_SEC_SIZE 2352 /* 2352 bytes */
+#define RAW16_SEC_SIZE (2352+16) /* 2368 bytes */
+#define RAW96_SEC_SIZE (2352+96) /* 2448 bytes */
+
+#define MAX_TRACK 99 /* Red Book track limit */
+
+#define PAD_SECS 15 /* NOTE: pad must be less than BUF_SIZE */
+#define PAD_SIZE (PAD_SECS * DATA_SEC_SIZE)
+
+/*
+ * FIFO size must be at least 2x CDR_MAX_BUF_SIZE
+ */
+#define DEFAULT_FIFOSIZE (4L*1024L*1024L)
+
+#if !defined(HAVE_LARGEFILES) && SIZEOF_LLONG > SIZEOF_LONG
+typedef Llong tsize_t;
+#else
+typedef off_t tsize_t;
+#endif
+
+#ifdef nono
+typedef struct tindex {
+ int dummy; /* Not yet implemented */
+} tindex_t;
+#endif
+
+typedef struct ofile {
+ struct ofile *next; /* Next open file */
+ int f; /* Open file */
+ char *filename; /* File name */
+ int refcnt; /* open reference count */
+} ofile_t;
+
+typedef struct track {
+ void *xfp; /* Open file for this track from xopen()*/
+ char *filename; /* File name for this track */
+
+ tsize_t itracksize; /* Size of track bytes (-1 == until EOF)*/
+ /* This is in units of isecsize */
+ tsize_t tracksize; /* Size of track bytes (-1 == until EOF)*/
+ /* This is in units of secsize */
+
+ long trackstart; /* Start sector # of this track */
+ long tracksecs; /* Size of this track (sectors) */
+ long padsecs; /* Pad size for this track (sectors) */
+ long pregapsize; /* Pre-gap size for this track (sectors)*/
+ long index0start; /* Index 0 start within this tr(sectors)*/
+ int isecsize; /* Read input sector size for this track*/
+ int secsize; /* Sector size for this track (bytes) */
+ int secspt; /* # of sectors to copy for one transfer*/
+ int pktsize; /* # of blocks per write packet */
+ Uchar dataoff; /* offset of user data in raw sector */
+ Uchar tracks; /* Number of tracks on this disk */
+ Uchar track; /* Track # as offset in track[] array */
+ Uchar trackno; /* Track # on disk for this track */
+ Uchar tracktype; /* Track type (toc type) */
+ Uchar dbtype; /* Data block type for this track */
+ int sectype; /* Sector type */
+ int flags; /* Flags (see below) */
+ int nindex; /* Number of indices for track */
+ long *tindex; /* Track index descriptor */
+ char *isrc; /* ISRC code for this track / disk MCN */
+ void *text; /* Opaque CD-Text data (txtptr_t *) */
+} track_t;
+
+#define track_base(tp) ((tp) - (tp)->track)
+
+/*
+ * Defines for tp->flags
+ */
+#define TI_AUDIO 0x00001 /* File is an audio track */
+#define TI_PREEMP 0x00002 /* Audio track recorded w/preemphasis */
+#define TI_MIX 0x00004 /* This is a mixed mode track */
+#define TI_RAW 0x00008 /* Write this track in raw mode */
+#define TI_PAD 0x00010 /* Pad data track */
+#define TI_SWAB 0x00020 /* Swab audio data */
+#define TI_ISOSIZE 0x00040 /* Use iso size for track */
+#define TI_NOAUHDR 0x00080 /* Don't look for audio header */
+#define TI_FIRST 0x00100 /* This is the first track */
+#define TI_LAST 0x00200 /* This is the last track */
+#define TI_PACKET 0x00400 /* Fixed- or variable-packet track */
+#define TI_NOCLOSE 0x00800 /* Don't close the track after writing */
+#define TI_TAO 0x01000 /* This track is written in TAO mode */
+#define TI_PREGAP 0x02000 /* Prev. track incl. pregap of this tr. */
+#define TI_SCMS 0x04000 /* Force to follow the SCMS rules */
+#define TI_COPY 0x08000 /* Allow digital copy */
+#define TI_SHORT_TRACK 0x10000 /* Ignore min 4 second Red Book std. */
+#define TI_RAW16 0x20000 /* This track uses 16 bytes subch. */
+#define TI_RAW96R 0x40000 /* This track uses 96 bytes RAW subch. */
+#define TI_CLONE 0x80000 /* Special clone treatment needed */
+#define TI_TEXT 0x100000 /* This track holds CD-Text information */
+#define TI_DVD 0x200000 /* We are writing a DVD track */
+#define TI_SAO 0x400000 /* This track is written in SAO mode */
+#define TI_USEINFO 0x800000 /* Use information from *.inf files */
+#define TI_QUADRO 0x1000000 /* Four Channel Audio Data */
+
+
+#define is_audio(tp) (((tp)->flags & TI_AUDIO) != 0)
+#define is_preemp(tp) (((tp)->flags & TI_PREEMP) != 0)
+#define is_pad(tp) (((tp)->flags & TI_PAD) != 0)
+#define is_swab(tp) (((tp)->flags & TI_SWAB) != 0)
+#define is_first(tp) (((tp)->flags & TI_FIRST) != 0)
+#define is_last(tp) (((tp)->flags & TI_LAST) != 0)
+#define is_packet(tp) (((tp)->flags & TI_PACKET) != 0)
+#define is_noclose(tp) (((tp)->flags & TI_NOCLOSE) != 0)
+#define is_tao(tp) (((tp)->flags & TI_TAO) != 0)
+#define is_sao(tp) (((tp)->flags & TI_SAO) != 0)
+#define is_raw(tp) (((tp)->flags & TI_RAW) != 0)
+#define is_raw16(tp) (((tp)->flags & TI_RAW16) != 0)
+#define is_raw96(tp) (((tp)->flags & (TI_RAW|TI_RAW16)) == TI_RAW)
+#define is_raw96p(tp) (((tp)->flags & (TI_RAW|TI_RAW16|TI_RAW96R)) == TI_RAW)
+#define is_raw96r(tp) (((tp)->flags & (TI_RAW|TI_RAW16|TI_RAW96R)) == (TI_RAW|TI_RAW96R))
+#define is_pregap(tp) (((tp)->flags & TI_PREGAP) != 0)
+#define is_scms(tp) (((tp)->flags & TI_SCMS) != 0)
+#define is_copy(tp) (((tp)->flags & TI_COPY) != 0)
+#define is_shorttrk(tp) (((tp)->flags & TI_SHORT_TRACK) != 0)
+#define is_clone(tp) (((tp)->flags & TI_CLONE) != 0)
+#define is_text(tp) (((tp)->flags & TI_TEXT) != 0)
+#define is_quadro(tp) (((tp)->flags & TI_QUADRO) != 0)
+
+/*
+ * Defines for toc type / track type
+ */
+#define TOC_DA 0 /* CD-DA */
+#define TOC_ROM 1 /* CD-ROM */
+#define TOC_XA1 2 /* CD_ROM XA with first track in mode 1 */
+#define TOC_XA2 3 /* CD_ROM XA with first track in mode 2 */
+#define TOC_CDI 4 /* CDI */
+
+#define TOC_MASK 7 /* Mask needed for toctname[] */
+
+/*
+ * Additional flags in toc type / trackp->tracktype
+ * XXX TOCF_DUMMY istr schon in dp->cdr_cmdflags & F_DUMMY
+ * XXX TOCF_MULTI istr schon in dp->cdr_cmdflags & F_MULTI
+ */
+#define TOCF_DUMMY 0x10 /* Write in dummy (simulation) mode */
+#define TOCF_MULTI 0x20 /* Multisession (Open Next Programarea) */
+
+#define TOCF_MASK 0xF0 /* All possible flags in tracktype */
+
+extern char *toc2name[]; /* Convert toc type to name */
+extern int toc2sess[]; /* Convert toc type to session format */
+
+/*
+ * Defines for sector type
+ *
+ * Mode is 2 bits
+ * Aud is 1 bit
+ *
+ * Sector is: aud << 2 | mode
+ */
+#define ST_ROM_MODE1 1 /* CD-ROM in mode 1 (vanilla cdrom) */
+#define ST_ROM_MODE2 2 /* CD-ROM in mode 2 */
+#define ST_AUDIO_NOPRE 4 /* CD-DA stereo without preemphasis */
+#define ST_AUDIO_PRE 5 /* CD-DA stereo with preemphasis */
+
+#define ST_PREEMPMASK 0x01 /* Mask for preemphasis bit */
+#define ST_AUDIOMASK 0x04 /* Mask for audio bit */
+#define ST_MODEMASK 0x03 /* Mask for mode bits in sector type */
+#define ST_MASK 0x07 /* Mask needed for sectname[] */
+
+/*
+ * There are 6 different generic basic sector types.
+ */
+#define ST_MODE_AUDIO 0x00 /* Generic Audio mode */
+#define ST_MODE_0 0x10 /* Generic Zero mode */
+#define ST_MODE_1 0x20 /* Generic CD-ROM mode (ISO/IEC 10149) */
+#define ST_MODE_2 0x30 /* Generic Mode 2 (ISO/IEC 10149) */
+#define ST_MODE_2_FORM_1 0x40 /* Generic Mode 2 form 1 */
+#define ST_MODE_2_FORM_2 0x50 /* Generic Mode 2 form 2 */
+#define ST_MODE_2_MIXED 0x60 /* Generic Mode 2 mixed form (1/2) */
+
+#define ST_MODE_MASK 0x70 /* Mask needed to get generic sectype */
+
+#define ST_MODE_RAW 0x08 /* Do not touch EDC & subchannels */
+#define ST_NOSCRAMBLE 0x80 /* Do not srcamble sectors */
+
+#define SECT_AUDIO (ST_AUDIO_NOPRE | ST_MODE_AUDIO)
+#define SECT_AUDIO_NOPRE (ST_AUDIO_NOPRE | ST_MODE_AUDIO)
+#define SECT_AUDIO_PRE (ST_AUDIO_PRE | ST_MODE_AUDIO)
+#define SECT_MODE_0 (ST_ROM_MODE1 | ST_MODE_0)
+#define SECT_ROM (ST_ROM_MODE1 | ST_MODE_1)
+#define SECT_MODE_2 (ST_ROM_MODE2 | ST_MODE_2)
+#define SECT_MODE_2_F1 (ST_ROM_MODE2 | ST_MODE_2_FORM_1)
+#define SECT_MODE_2_F2 (ST_ROM_MODE2 | ST_MODE_2_FORM_2)
+#define SECT_MODE_2_MIX (ST_ROM_MODE2 | ST_MODE_2_MIXED)
+
+extern char *st2name[]; /* Convert sector type to name */
+extern int st2mode[]; /* Convert sector type to control nibble*/
+
+/*
+ * Control nibble bits:
+ *
+ * 0 with preemphasis (audio) / incremental (data)
+ * 1 digital copy permitted
+ * 2 data (not audio) track
+ * 3 4 channels (not 2)
+ */
+#define TM_PREEM 0x1 /* Audio track with preemphasis */
+#define TM_INCREMENTAL 0x1 /* Incremental data track */
+#define TM_ALLOW_COPY 0x2 /* Digital copy permitted */
+#define TM_DATA 0x4 /* This is a data track */
+#define TM_QUADRO 0x8 /* Four channel audio */
+
+/*
+ * Adr nibble:
+ */
+#define ADR_NONE 0 /* Sub-Q mode info not supplied */
+#define ADR_POS 1 /* Sub-Q encodes position data */
+#define ADR_MCN 2 /* Sub-Q encodes Media Catalog Number */
+#define ADR_ISRC 3 /* Sub-Q encodes ISRC */
+
+/*
+ * Defines for write type (from SCSI-3/mmc)
+ */
+#define WT_PACKET 0x0 /* Packet writing */
+#define WT_TAO 0x1 /* Track at once */
+#define WT_SAO 0x2 /* Session at once */
+#define WT_RAW 0x3 /* Raw */
+#define WT_RES_4 0x4 /* Reserved */
+#define WT_RES_5 0x5 /* Reserved */
+#define WT_RES_6 0x6 /* Reserved */
+#define WT_RES_7 0x7 /* Reserved */
+#define WT_RES_8 0x8 /* Reserved */
+#define WT_RES_9 0x9 /* Reserved */
+#define WT_RES_A 0xA /* Reserved */
+#define WT_RES_B 0xB /* Reserved */
+#define WT_RES_C 0xC /* Reserved */
+#define WT_RES_D 0xD /* Reserved */
+#define WT_RES_E 0xE /* Reserved */
+#define WT_RES_F 0xF /* Reserved */
+
+/*
+ * Data block layout:
+ *
+ * - Sync pattern 12 Bytes: 0x00 0xFF 0xFF ... 0xFF 0xFF 0x00
+ * - Block header 4 Bytes: | minute | second | frame | mode |
+ * Mode byte:
+ * Bits 7, 6, 5 Run-in/Run-out/Link
+ * Bits 4, 3, 2 Reserved
+ * Bits 1, 0 Mode
+ * - Rest of sector see below.
+ *
+ * Mode 0 Format:
+ * 0 12 Bytes Sync header
+ * 12 4 Bytes Block header with Data mode == 0
+ * 16 2336 Bytes of zero data
+ *
+ * Mode 1 Format:
+ * 0 12 Bytes Sync header
+ * 12 4 Bytes Block header with Data mode == 1
+ * 16 2048 Bytes of user data
+ * 2064 4 Bytes CRC for Bytes 0-2063
+ * 2068 8 Bytes Zero fill
+ * 2076 172 Bytes P parity symbols
+ * 2248 104 Bytes Q parity symbols
+ *
+ * Mode 2 Format (formless):
+ * 0 12 Bytes Sync header
+ * 12 4 Bytes Block header with Data mode == 2
+ * 16 2336 Bytes of user data
+ *
+ * Mode 2 form 1 Format:
+ * 0 12 Bytes Sync header
+ * 12 4 Bytes Block header with Data mode == 2
+ * 16 4 Bytes subheader first copy
+ * 20 4 Bytes subheader second copy
+ * 24 2048 Bytes of user data
+ * 2072 4 Bytes CRC for Bytes 16-2071
+ * 2076 172 Bytes P parity symbols
+ * 2248 104 Bytes Q parity symbols
+ *
+ * Mode 2 form 2 Format:
+ * 0 12 Bytes Sync header
+ * 12 4 Bytes Block header with Data mode == 2
+ * 16 4 Bytes subheader first copy
+ * 20 4 Bytes subheader second copy
+ * 24 2324 Bytes of user data
+ * 2348 4 Bytes Optional CRC for Bytes 16-2347
+ */
+
+/*
+ * Mode Byte definitions (the 4th Byte in the Block header)
+ */
+#define SH_MODE_DATA 0x00 /* User Data Block */
+#define SH_MODE_RI4 0x20 /* Fourth run in Block */
+#define SH_MODE_RI3 0x40 /* Third run in Block */
+#define SH_MODE_RI2 0x60 /* Second run in Block */
+#define SH_MODE_RI1 0x80 /* First run in Block */
+#define SH_MODE_LINK 0xA0 /* Link Block */
+#define SH_MODE_RO2 0xC0 /* Second run out Block */
+#define SH_MODE_RO1 0xE0 /* First run out Block */
+#define SH_MODE_M0 0x00 /* Mode 0 Data */
+#define SH_MODE_M1 0x01 /* Mode 1 Data */
+#define SH_MODE_M2 0x02 /* Mode 2 Data */
+#define SH_MODE_MR 0x03 /* Reserved */
+
+/*
+ * Defines for data block type (from SCSI-3/mmc)
+ *
+ * Mandatory are only:
+ * DB_ROM_MODE1 (8) Mode 1 (ISO/IEC 10149)
+ * DB_XA_MODE2 (10) Mode 2-F1 (CD-ROM XA form 1)
+ * DB_XA_MODE2_MIX (13) Mode 2-MIX (CD-ROM XA 1/2+subhdr)
+ */
+#define DB_RAW 0 /* 2352 bytes of raw data */
+#define DB_RAW_PQ 1 /* 2368 bytes (raw data + P/Q Subchannel) */
+#define DB_RAW_PW 2 /* 2448 bytes (raw data + P-W Subchannel) */
+#define DB_RAW_PW_R 3 /* 2448 bytes (raw data + P-W raw Subchannel)*/
+#define DB_RES_4 4 /* - Reserved */
+#define DB_RES_5 5 /* - Reserved */
+#define DB_RES_6 6 /* - Reserved */
+#define DB_VU_7 7 /* - Vendor specific */
+#define DB_ROM_MODE1 8 /* 2048 bytes Mode 1 (ISO/IEC 10149) */
+#define DB_ROM_MODE2 9 /* 2336 bytes Mode 2 (ISO/IEC 10149) */
+#define DB_XA_MODE2 10 /* 2048 bytes Mode 2 (CD-ROM XA form 1) */
+#define DB_XA_MODE2_F1 11 /* 2056 bytes Mode 2 (CD-ROM XA form 1) */
+#define DB_XA_MODE2_F2 12 /* 2324 bytes Mode 2 (CD-ROM XA form 2) */
+#define DB_XA_MODE2_MIX 13 /* 2332 bytes Mode 2 (CD-ROM XA 1/2+subhdr) */
+#define DB_RES_14 14 /* - Reserved */
+#define DB_VU_15 15 /* - Vendor specific */
+
+extern char *db2name[]; /* Convert data block type to name */
+
+/*
+ * Defines for multi session type (from SCSI-3/mmc)
+ */
+#define MS_NONE 0 /* No B0 pointer. Next session not allowed*/
+#define MS_FINAL 1 /* B0 = FF:FF:FF. Next session not allowed*/
+#define MS_RES 2 /* Reserved */
+#define MS_MULTI 3 /* B0 = Next PA. Next session allowed */
+
+/*
+ * Defines for session format (from SCSI-3/mmc)
+ */
+#define SES_DA_ROM 0x00 /* CD-DA or CD-ROM disk */
+#define SES_CDI 0x10 /* CD-I disk */
+#define SES_XA 0x20 /* CD-ROM XA disk */
+#define SES_UNDEF 0xFF /* Undefined disk type (read disk info) */
+
+/*
+ * Defines for blanking of CD-RW discs (from SCSI-3/mmc)
+ */
+#define BLANK_DISC 0x00 /* Erase the entire disc */
+#define BLANK_MINIMAL 0x01 /* Erase the PMA, 1st session TOC, pregap */
+#define BLANK_TRACK 0x02 /* Erase an incomplete track */
+#define BLANK_UNRESERVE 0x03 /* Unreserve a track */
+#define BLANK_TAIL 0x04 /* Erase the tail of a track */
+#define BLANK_UNCLOSE 0x05 /* Unclose the last session */
+#define BLANK_SESSION 0x06 /* Erase the last session */
+
+/*
+ * Defines for formating DVD (custom values)
+ */
+#define FULL_FORMAT 0x00 /* Interactive format */
+#define BACKGROUND_FORMAT 0x01 /* Background format */
+#define FORCE_FORMAT 0x02 /* Force reformat */
+
+/*
+ * Defines for formating DVD (custom values)
+ */
+#define FULL_FORMAT 0x00 /* Interactive format */
+#define BACKGROUND_FORMAT 0x01 /* Background format */
+#define FORCE_FORMAT 0x02 /* Force reformat */
+
+/*
+ * Useful definitions for audio tracks
+ */
+#define msample (44100 * 2) /* one 16bit audio sample */
+#define ssample (msample * 2) /* one stereo sample */
+#define samples(v) ((v) / ssample) /* # of stereo samples */
+#define hsamples(v) ((v) / (ssample/100)) /* 100* # of stereo samples/s*/
+#define fsamples(v) ((v) / (ssample/75)) /* 75* # of stereo samples/s */
+
+#define minutes(v) ((int)(samples(v) / 60))
+#define seconds(v) ((int)(samples(v) % 60))
+#define hseconds(v) ((int)(hsamples(v) % 100))
+#define frames(v) ((int)(fsamples(v) % 75))
+
+/*
+ * sector based macros
+ */
+#define Sminutes(s) ((int)((s) / (60*75)))
+#define Sseconds(s) ((int)((s) / 75))
+#define Shseconds(s) ((int)(((s) % 75)*100)/75)
+#define Sframes(s) ((int)((s) % 75))
+
+typedef struct msf {
+ char msf_min;
+ char msf_sec;
+ char msf_frame;
+} msf_t;
+
+/*
+ * Definitions for read TOC/PMA/ATIP command
+ */
+#define FMT_TOC 0
+#define FMT_SINFO 1
+#define FMT_FULLTOC 2
+#define FMT_PMA 3
+#define FMT_ATIP 4
+#define FMT_CDTEXT 5
+
+/*
+ * Definitions for read disk information "recording flags"
+ * used in UInt16_t "ds_cdrflags".
+ */
+#define RF_WRITE 0x0001 /* Disk is going to be written */
+#define RF_BLANK 0x0002 /* Disk is going to be erased */
+#define RF_PRATIP 0x0004 /* Print ATIP info */
+#define RF_LEADIN 0x0008 /* Lead-in has been "manually" written */
+#define RF_BURNFREE 0x0010 /* BUFFER underrun free recording */
+#define RF_VARIREC 0x0020 /* Plextor VariRec */
+#define RF_AUDIOMASTER 0x0040 /* Yamaha AudioMaster */
+#define RF_FORCESPEED 0x0080 /* WriteSpeed forced high */
+#define RF_DID_STAT 0x0100 /* Already did call cdrstats() */
+#define RF_DID_CDRSTAT 0x0200 /* Already did call (*dp->cdr_stats)() */
+#define RF_WR_WAIT 0x0400 /* Wait during writing to free bus */
+#define RF_SINGLESESS 0x0800 /* Plextor single sess. mode */
+#define RF_HIDE_CDR 0x1000 /* Plextor hide CDR features */
+#define RF_SPEEDREAD 0x2000 /* Plextor SpeedReed */
+#define RF_GIGAREC 0x4000 /* Plextor GigaRec */
+
+/*
+ * Definitions for read disk information "disk status"
+ * used in "ds_diskstat".
+ */
+#define DS_EMPTY 0 /* Empty disk */
+#define DS_APPENDABLE 1 /* Incomplete disk (appendable) */
+#define DS_COMPLETE 2 /* Complete disk (closed/no B0 pointer) */
+#define DS_RESERVED 3 /* Reserved */
+
+/*
+ * Definitions for read disk information "session status"
+ * used in "ds_sessstat".
+ */
+#define SS_EMPTY 0 /* Empty session */
+#define SS_APPENDABLE 1 /* Incomplete session */
+#define SS_RESERVED 2 /* Reserved */
+#define SS_COMPLETE 3 /* Complete session (needs DS_COMPLETE) */
+
+/*
+ * Definitions for disk_status write mode
+ * used in "ds_wrmode".
+ */
+#define WM_NONE 0 /* No write mode selected */
+#define WM_BLANK 1 /* Blanking mode */
+#define WM_FORMAT 2 /* Formatting */
+#define WM_PACKET 4 /* Packet writing */
+#define WM_TAO 8 /* Track at Once */
+#define WM_SAO 12 /* Session at Once w/ cooked sectors */
+#define WM_SAO_RAW16 13 /* Session at Once RAW+16 byte sectors */
+#define WM_SAO_RAW96P 14 /* Session at Once RAW+96P byte sectors */
+#define WM_SAO_RAW96R 15 /* Session at Once RAW+96R byte sectors */
+#define WM_RAW 16 /* RAW with cooked sectors is impossible*/
+#define WM_RAW_RAW16 17 /* RAW with RAW+16 byte sectors */
+#define WM_RAW_RAW96P 18 /* RAW with RAW+96P byte sectors */
+#define WM_RAW_RAW96R 19 /* RAW with RAW+96R byte sectors */
+
+#define wm_base(wm) ((wm)/4*4) /* The basic write mode for this mode */
+
+/*
+ * Definitions for disk_status flags
+ * used in UInt16_t "ds_flags".
+ */
+#define DSF_DID_V 0x0001 /* Disk id valid */
+#define DSF_DBC_V 0x0002 /* Disk bar code valid */
+#define DSF_URU 0x0004 /* Disk is for unrestricted use */
+#define DSF_ERA 0x0008 /* Disk is erasable */
+#define DSF_HIGHSP_ERA 0x0010 /* Disk is high speed erasable */
+#define DSF_ULTRASP_ERA 0x0020 /* Disk is ultra speed erasable */
+#define DSF_ULTRASPP_ERA 0x0040 /* Disk is ultra speed+ erasable */
+
+
+#define DSF_DVD 0x0100 /* Disk is a DVD */
+#define DSF_DVD_PLUS_R 0x0200 /* Disk is a DVD+R */
+#define DSF_DVD_PLUS_RW 0x0400 /* Disk is a DVD+RW */
+#define DSF_NEED_FORMAT 0x0800 /* Disk needs to be formatted */
+
+/*
+ * Definitions for disktype flags
+ */
+#define DT_CD 0x001 /*is a CD */
+#define DT_DVD 0x002 /*is a DVD */
+
+/*
+ * Definitions for disktype flags
+ */
+#define DT_CD 0x001 /*is a CD */
+#define DT_DVD 0x002 /*is a DVD */
+
+/*
+ * Definitions for disk_status disk type
+ * used in "ds_type".
+ */
+/* None defined yet */
+
+typedef struct disk_status dstat_t;
+
+struct disk_status {
+ UInt32_t ds_diskid; /* Disk identification */
+ UInt16_t ds_cdrflags; /* Recording flags from cdrecord*/
+ UInt16_t ds_flags; /* Disk_status flags */
+ Uchar ds_wrmode; /* Selected write mode */
+ Uchar ds_type; /* Abstract disk type */
+
+ Uchar ds_disktype; /* Disk type (from TOC/PMA) */
+ Uchar ds_diskstat; /* Disk status (MMC) */
+ Uchar ds_sessstat; /* Status of last sesion (MMC) */
+ Uchar ds_trfirst; /* first track # */
+ Uchar ds_trlast; /* last track # */
+ Uchar ds_trfirst_ls; /* first track # in last session*/
+ Uchar ds_barcode[8]; /* Disk bar code */
+
+ Int32_t ds_first_leadin; /* Start of first lead in (ATIP)*/
+ Int32_t ds_last_leadout; /* Start of last lead out (ATIP)*/
+ Int32_t ds_curr_leadin; /* Start of next lead in */
+ Int32_t ds_curr_leadout; /* Start of next lead out */
+
+ Int32_t ds_maxblocks; /* # of official blocks on disk */
+ Int32_t ds_maxrblocks; /* # real blocks on disk */
+ Int32_t ds_fwa; /* first writable addr */
+
+ Int32_t ds_startsec; /* Actual start sector */
+ Int32_t ds_endsec; /* Actual end sector */
+ Int32_t ds_buflow; /* # of times drive buffer empty*/
+
+ UInt16_t ds_minbuf; /* Minimum drive bufer fill rt. */
+
+ UInt16_t ds_at_min_speed; /* The minimal ATIP write speed */
+ UInt16_t ds_at_max_speed; /* The maximal ATIP write speed */
+ UInt16_t ds_dr_cur_rspeed; /* The drive's cur read speed */
+ UInt16_t ds_dr_max_rspeed; /* The drive's max read speed */
+ UInt16_t ds_dr_cur_wspeed; /* The drive's cur write speed */
+ UInt16_t ds_dr_max_wspeed; /* The drive's max write speed */
+ UInt16_t ds_wspeed; /* The selected/drive wr. speed */
+};
+
+/*
+ * First approach of a CDR device abstraction layer.
+ * This interface will change as long as I did not find the
+ * optimum that fits for all devices.
+ *
+ * Called with pointer to whole track array:
+ * cdr_send_cue()
+ * cdr_write_leadin()
+ * cdr_open_session()
+ * cdr_fixate()
+ *
+ * Called with (track_t *) 0 or pointer to current track:
+ * cdr_next_wr_address()
+ *
+ * Called with pointer to current track:
+ * cdr_open_track()
+ * cdr_close_track()
+ *
+ * Calling sequence:
+ * cdr_identify() May modify driver
+ * Here, the cdr_t will be allocated and
+ * copied to a new writable area.
+ * cdr_attach() Get drive properties
+ * cdr_buffer_cap()
+ * cdr_getdisktype() GET ATIP
+ * cdr_init() set TAO for -msinfo
+ * cdr_check_session XXX ????
+ * cdr_opt1() set early options
+ * cdr_set_speed_dummy(usalp, dp, &speed)
+ * <--- Grace time processing goes here
+ * { do_opc(); cdr_blank() }
+ * cdr_opt2() set late options
+ * cdr_open_session() set up params (no wrt.)
+ * do_opc()
+ * cdr_write_leadin() start writing
+ * LOOP {
+ * cdr_open_track()
+ * cdr_next_wr_address() only TAO / Packet
+ * write_track_data()
+ * cdr_close_track()
+ * }
+ * write_leadout() XXX should go -> driver!
+ * cdr_fixate()
+ * cdr_stats()
+ */
+/*--------------------------------------------------------------------------*/
+typedef struct cdr_cmd cdr_t;
+
+#ifdef _SCG_SCSITRANSP_H
+struct cdr_cmd {
+ int cdr_dev; /* Numerical device type */
+ UInt32_t cdr_cmdflags; /* Command line options */
+ UInt32_t cdr_flags; /* Drive related flags */
+ UInt8_t cdr_cdrw_support; /* CD-RW write media types */
+ UInt16_t cdr_speeddef; /* Default write speed */
+ UInt16_t cdr_speedmax; /* Max. write speed */
+
+ char *cdr_drname; /* Driver ID string */
+ char *cdr_drtext; /* Driver ID text */
+ struct cd_mode_page_2A *cdr_cdcap;
+ dstat_t *cdr_dstat;
+#ifdef _SCG_SCSIREG_H
+ /* identify drive */
+ cdr_t *(*cdr_identify)(SCSI *usalp, cdr_t *, struct scsi_inquiry *);
+#else
+ /* identify drive */
+ cdr_t *(*cdr_identify)(SCSI *usalp, cdr_t *, void *);
+#endif
+ /* init error decoding etc*/
+ int (*cdr_attach)(SCSI *usalp, cdr_t *);
+ /* init drive to useful deflts */
+ int (*cdr_init)(SCSI *usalp, cdr_t *);
+ /* get disk type */
+ int (*cdr_getdisktype)(SCSI *usalp, cdr_t *);
+ /* load disk */
+ int (*cdr_load)(SCSI *usalp, cdr_t *);
+ /* unload disk */
+ int (*cdr_unload)(SCSI *usalp, cdr_t *);
+ /* read buffer capacity */
+ int (*cdr_buffer_cap)(SCSI *usalp, long *sizep, long *freep);
+ /* check if recover is needed */
+ int (*cdr_check_recovery)(SCSI *usalp, cdr_t *);
+ /* do recover */
+ int (*cdr_recover)(SCSI *usalp, cdr_t *, int track);
+ /* set recording speed & dummy write */
+ int (*cdr_set_speed_dummy)(SCSI *usalp, cdr_t *, int *speedp);
+ /* set sector size */
+ int (*cdr_set_secsize)(SCSI *usalp, int secsize);
+ /* get next writable addr. */
+ int (*cdr_next_wr_address)(SCSI *usalp, track_t *trackp, long *ap);
+ /* reserve track for future use */
+ int (*cdr_reserve_track)(SCSI *usalp, Ulong len);
+ int (*cdr_write_trackdata)(SCSI *usalp, caddr_t buf, long daddr, long bytecnt,
+ int seccnt, BOOL islast);
+ /* generate cue sheet */
+ int (*cdr_gen_cue)(track_t *trackp, void *cuep, BOOL needgap);
+ /* send cue sheet */
+ int (*cdr_send_cue)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* write leadin */
+ int (*cdr_write_leadin)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* open new track */
+ int (*cdr_open_track)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* close written track */
+ int (*cdr_close_track)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* open new session */
+ int (*cdr_open_session)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* really needed ??? */
+ int (*cdr_close_session)(SCSI *usalp, cdr_t *);
+ /* abort current write */
+ int (*cdr_abort_session)(SCSI *usalp, cdr_t *);
+ /* read session offset*/
+ int (*cdr_session_offset)(SCSI *usalp, long *soff);
+ /* write toc on disk */
+ int (*cdr_fixate)(SCSI *usalp, cdr_t *, track_t *trackp);
+ /* final statistics printing*/
+ int (*cdr_stats)(SCSI *usalp, cdr_t *);
+ /* blank something */
+ int (*cdr_blank)(SCSI *usalp, cdr_t *, long addr, int blanktype);
+ /* format media */
+ int (*cdr_format)(SCSI *usalp, cdr_t *, int fmtflags);
+ /* Do OPC */
+ int (*cdr_opc)(SCSI *usalp, caddr_t bp, int cnt, int doopc);
+ /* do early option processing*/
+ int (*cdr_opt1)(SCSI *usalp, cdr_t *);
+ /* do late option processing */
+ int (*cdr_opt2)(SCSI *usalp, cdr_t *);
+ /* calculate optimale split */
+ int (*cdr_layer_split)(SCSI *usalp, cdr_t *, long tsize);
+ int profile;
+ BOOL is_dvd;
+};
+#endif
+
+/*
+ * Definitions for cdr_flags
+ */
+#define CDR_TAO 0x01 /* Drive supports Track at once */
+#define CDR_SAO 0x02 /* Drive supports Sess at once */
+#define CDR_PACKET 0x04 /* Drive supports packet writing*/
+#define CDR_RAW 0x08 /* Drive supports raw writing */
+#define CDR_RAW16 0x10 /* Drive supports RAW raw16 */
+#define CDR_RAW96P 0x20 /* Drive supports RAW raw96 pak */
+#define CDR_RAW96R 0x40 /* Drive supports RAW raw96 raw */
+#ifdef __needed__
+#define CDR_SRAW16 0x100 /* Drive supports SAO raw16 */
+#endif
+#define CDR_SRAW96P 0x200 /* Drive supports SAO raw96 pak */
+#define CDR_SRAW96R 0x400 /* Drive supports SAO raw96 raw */
+#define CDR_SWABAUDIO 0x1000 /* Drive swabs audio data */
+#define CDR_ISREADER 0x2000 /* Drive is s CD-ROM reader */
+#define CDR_TRAYLOAD 0x4000 /* Drive loads CD with tray */
+#define CDR_CADDYLOAD 0x8000 /* Drive loads CD with caddy */
+#define CDR_NO_LOLIMIT 0x10000 /* Drive ignores lead-out limit */
+#define CDR_DVD 0x20000 /* Drive is a DVD drive */
+#define CDR_SIMUL 0x40000 /* Drive is simulated */
+#define CDR_BURNFREE 0x80000 /* Drive sup. BUFund. free rec. */
+#define CDR_VARIREC 0x100000 /* Drive sup. VariRec Plex. */
+#define CDR_AUDIOMASTER 0x200000 /* Drive sup. AudioMaster Yamah.*/
+#define CDR_FORCESPEED 0x400000 /* Drive sup. WriteSpeed ctl. */
+#define CDR_DISKTATTOO 0x800000 /* Drive sup. Yamaha DiskT@2 */
+#define CDR_SINGLESESS 0x1000000 /* Drive sup. single sess. mode */
+#define CDR_HIDE_CDR 0x2000000 /* Drive sup. hide CDR features */
+#define CDR_SPEEDREAD 0x4000000 /* Drive sup. SpeedReed */
+#define CDR_GIGAREC 0x8000000 /* Drive sup. GigaRec Plex. */
+#define CDR_MMC 0x10000000 /* Drive is MMC compliant */
+#define CDR_MMC2 0x20000000 /* Drive is MMC-2 compliant */
+#define CDR_MMC3 0x40000000 /* Drive is MMC-3 compliant */
+#ifdef PROTOTYPES
+#define CDR_ALLOC 0x80000000UL /* structure is allocated */
+#else
+#define CDR_ALLOC 0x80000000 /* structure is allocated */
+#endif
+
+/*
+ * Definitions for cdr_cdrw_support
+ */
+#define CDR_CDRW_NONE 0x00 /* CD-RW writing not supported */
+#define CDR_CDRW_MULTI 0x01 /* CD-RW multi speed supported */
+#define CDR_CDRW_HIGH 0x02 /* CD-RW high speed supported */
+#define CDR_CDRW_ULTRA 0x04 /* CD-RW ultra high speed supported */
+#define CDR_CDRW_ULTRAP 0x08 /* CD-RW ultra high speed+ supported */
+#define CDR_CDRW_ALL 0xFF /* All bits set: unknown - support all */
+
+/*
+ * cdrecord.c
+ */
+extern int read_buf(int f, char *bp, int size);
+extern int fill_buf(int f, track_t *trackp, long secno, char *bp, int size);
+extern int get_buf(int f, track_t *trackp, long secno, char **bpp, int size);
+#ifdef _SCG_SCSITRANSP_H
+extern int write_secs(SCSI *usalp, cdr_t *dp, char *bp, long startsec,
+ int bytespt, int secspt, BOOL islast);
+extern int pad_track(SCSI *usalp, cdr_t *dp, track_t *trackp,
+ long startsec, Llong amt,
+ BOOL dolast, Llong *bytesp);
+extern void load_media(SCSI *usalp, cdr_t *, BOOL);
+extern void unload_media(SCSI *usalp, cdr_t *, int);
+extern void reload_media(SCSI *usalp, cdr_t *);
+#endif
+extern void raisepri(int);
+extern int getnum(char *arg, long *valp);
+
+/*
+ * cd_misc.c
+ */
+extern int from_bcd(int b);
+extern int to_bcd(int i);
+extern long msf_to_lba(int m, int s, int f, BOOL force_positive);
+extern BOOL lba_to_msf(long lba, msf_t *mp);
+extern void sec_to_msf(long sec, msf_t *mp);
+extern void print_min_atip(long li, long lo);
+
+/*
+ * fifo.c
+ */
+extern void init_fifo(long);
+extern BOOL init_faio(track_t *track, int);
+extern BOOL await_faio(void);
+extern void kill_faio(void);
+extern int wait_faio(void);
+extern int faio_read_buf(int f, char *bp, int size);
+extern int faio_get_buf(int f, char **bpp, int size);
+extern void fifo_stats(void);
+extern int fifo_percent(BOOL addone);
+
+/*
+ * wm_session.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern int write_session_data(SCSI *usalp, cdr_t *dp, track_t *trackp);
+#endif
+
+/*
+ * wm_track.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+/*extern int write_track_data __PR((SCSI *usalp, cdr_t *dp, track_t *trackp));*/
+#endif
+
+/*
+ * wm_packet.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern int write_packet_data(SCSI *usalp, cdr_t *dp, track_t *trackp);
+#endif
+
+/*
+ * modes.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern BOOL get_mode_params(SCSI *usalp, int page, char *pagename,
+ Uchar *modep, Uchar *cmodep,
+ Uchar *dmodep, Uchar *smodep,
+ int *lenp);
+extern BOOL set_mode_params(SCSI *usalp, char *pagename, Uchar *modep,
+ int len, int save, int secsize);
+#endif
+
+/*
+ * misc.c
+ */
+#ifdef timerclear
+extern void timevaldiff(struct timeval *start, struct timeval *stop);
+extern void prtimediff(const char *fmt, struct timeval *start,
+ struct timeval *stop);
+#endif
+
+/*
+ * getnum.c
+ */
+extern int getnum(char *arg, long *valp);
+extern int getllnum(char *arg, Llong *lvalp);
+
+/*
+ * scsi_cdr.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern BOOL unit_ready(SCSI *usalp);
+extern BOOL wait_unit_ready(SCSI *usalp, int secs);
+extern BOOL scsi_in_progress(SCSI *usalp);
+extern BOOL cdr_underrun(SCSI *usalp);
+extern int test_unit_ready(SCSI *usalp);
+extern int rezero_unit(SCSI *usalp);
+extern int request_sense(SCSI *usalp);
+extern int request_sense_b(SCSI *usalp, caddr_t bp, int cnt);
+extern int inquiry(SCSI *usalp, caddr_t, int);
+extern int read_capacity(SCSI *usalp);
+#ifdef EOF /* stdio.h has been included */
+extern void print_capacity(SCSI *usalp, FILE *f);
+#endif
+extern int scsi_load_unload(SCSI *usalp, int);
+extern int scsi_prevent_removal(SCSI *usalp, int);
+extern int scsi_start_stop_unit(SCSI *usalp, int, int, BOOL immed);
+
+#define ROTCTL_CLV 0 /* CLV or PCAV */
+#define ROTCTL_CAV 1 /* True CAV */
+
+extern int scsi_set_speed(SCSI *usalp, int readspeed, int writespeed,
+ int rotctl);
+extern int scsi_get_speed(SCSI *usalp, int *readspeedp, int *writespeedp);
+extern int qic02(SCSI *usalp, int);
+extern int write_xscsi(SCSI *usalp, caddr_t, long, long, int);
+extern int write_xg0(SCSI *usalp, caddr_t, long, long, int);
+extern int write_xg1(SCSI *usalp, caddr_t, long, long, int);
+extern int write_xg5(SCSI *usalp, caddr_t, long, long, int);
+extern int seek_scsi(SCSI *usalp, long addr);
+extern int seek_g0(SCSI *usalp, long addr);
+extern int seek_g1(SCSI *usalp, long addr);
+extern int scsi_flush_cache(SCSI *usalp, BOOL immed);
+extern int read_buffer(SCSI *usalp, caddr_t bp, int cnt, int mode);
+extern int write_buffer(SCSI *usalp, char *buffer, long length, int mode,
+ int bufferid, long offset);
+extern int read_subchannel(SCSI *usalp, caddr_t bp, int track, int cnt,
+ int msf, int subq, int fmt);
+extern int read_toc(SCSI *usalp, caddr_t, int, int, int, int);
+extern int read_toc_philips(SCSI *usalp, caddr_t, int, int, int, int);
+extern int read_header(SCSI *usalp, caddr_t, long, int, int);
+extern int read_disk_info(SCSI *usalp, caddr_t, int);
+
+#define TI_TYPE_LBA 0 /* Address is LBA */
+#define TI_TYPE_TRACK 1 /* Address: 0 -> TOC, xx -> Track xx, 0xFF -> Inv Track */
+#define TI_TYPE_SESS 2 /* Address is session # */
+extern int read_track_info(SCSI *usalp, caddr_t, int type, int addr, int cnt);
+extern int read_rzone_info(SCSI *usalp, caddr_t bp, int cnt);
+extern int reserve_tr_rzone(SCSI *usalp, long size);
+extern int read_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int addr,
+ int layer, int fmt);
+extern int send_dvd_structure(SCSI *usalp, caddr_t bp, int cnt, int layer,
+ int fmt);
+extern int send_opc(SCSI *usalp, caddr_t, int cnt, int doopc);
+
+#define CL_TYPE_STOP_DEICE 0 /* Stop De-icing a DVD+RW Media */
+#define CL_TYPE_TRACK 1 /* Close Track # */
+#define CL_TYPE_SESSION 2 /* Close Session/Border / Stop backgrnd. format */
+#define CL_TYPE_INTER_BORDER 3 /* Close intermediate Border */
+#define CL_TYPE_OPEN_SESSION 4 /* Close the Open Session and Record an Extended lead-out */
+#define CL_TYPE_FINALISE_MINRAD 5 /* Finalize the Disc with a Minimum Recorded Radius */
+#define CL_TYPE_FINALISE 6 /* Finalize the disc */
+extern int scsi_close_tr_session(SCSI *usalp, int type, int track,
+ BOOL immed);
+extern int read_master_cue(SCSI *usalp, caddr_t bp, int sheet, int cnt);
+extern int send_cue_sheet(SCSI *usalp, caddr_t bp, long size);
+extern int read_buff_cap(SCSI *usalp, long *, long *);
+extern int scsi_blank(SCSI *usalp, long addr, int blanktype, BOOL immed);
+extern BOOL allow_atapi(SCSI *usalp, BOOL new);
+extern int mode_select(SCSI *usalp, Uchar *, int, int, int);
+extern int mode_sense(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+extern int mode_select_sg0(SCSI *usalp, Uchar *, int, int, int);
+extern int mode_sense_sg0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+extern int mode_select_g0(SCSI *usalp, Uchar *, int, int, int);
+extern int mode_select_g1(SCSI *usalp, Uchar *, int, int, int);
+extern int mode_sense_g0(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+extern int mode_sense_g1(SCSI *usalp, Uchar *dp, int cnt, int page, int pcf);
+extern int read_tochdr(SCSI *usalp, cdr_t *, int *, int *);
+extern int read_cdtext(SCSI *usalp);
+extern int read_trackinfo(SCSI *usalp, int, long *, struct msf *, int *,
+ int *, int *);
+extern int read_B0(SCSI *usalp, BOOL isbcd, long *b0p, long *lop);
+extern int read_session_offset(SCSI *usalp, long *);
+extern int read_session_offset_philips(SCSI *usalp, long *);
+extern int sense_secsize(SCSI *usalp, int current);
+extern int select_secsize(SCSI *usalp, int);
+extern BOOL is_cddrive(SCSI *usalp);
+extern BOOL is_unknown_dev(SCSI *usalp);
+extern int read_scsi(SCSI *usalp, caddr_t, long, int);
+extern int read_g0(SCSI *usalp, caddr_t, long, int);
+extern int read_g1(SCSI *usalp, caddr_t, long, int);
+extern BOOL getdev(SCSI *usalp, BOOL);
+#ifdef EOF /* stdio.h has been included */
+extern void printinq(SCSI *usalp, FILE *f);
+#endif
+extern void printdev(SCSI *usalp);
+extern BOOL do_inquiry(SCSI *usalp, BOOL);
+extern BOOL recovery_needed(SCSI *usalp, cdr_t *);
+extern int scsi_load(SCSI *usalp, cdr_t *);
+extern int scsi_unload(SCSI *usalp, cdr_t *);
+extern int scsi_cdr_write(SCSI *usalp, caddr_t bp, long sectaddr,
+ long size, int blocks, BOOL islast);
+extern struct cd_mode_page_2A *mmc_cap(SCSI *usalp, Uchar *modep);
+extern void mmc_getval(struct cd_mode_page_2A *mp, BOOL *cdrrp, BOOL *cdwrp,
+ BOOL *cdrrwp, BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp);
+extern BOOL is_mmc(SCSI *usalp, BOOL *cdwp, BOOL *dvdwp);
+extern BOOL mmc_check(SCSI *usalp, BOOL *cdrrp, BOOL *cdwrp, BOOL *cdrrwp,
+ BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp);
+extern void print_capabilities(SCSI *usalp);
+#endif
+
+/*
+ * scsi_cdr.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern void print_capabilities_mmc4(SCSI *usalp);
+#endif
+
+/*
+ * scsi_mmc.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern int get_configuration(SCSI *usalp, caddr_t bp, int cnt,
+ int st_feature, int rt);
+extern int get_curprofile(SCSI *usalp);
+extern int print_profiles(SCSI *usalp);
+extern int get_proflist(SCSI *usalp, BOOL *wp, BOOL *cdp, BOOL *dvdp,
+ BOOL *dvdplusp, BOOL *ddcdp);
+extern int get_wproflist(SCSI *usalp, BOOL *cdp, BOOL *dvdp,
+ BOOL *dvdplusp, BOOL *ddcdp);
+extern char *mmc_obtain_profile_name(int profile_number);
+#endif
+
+/*
+ * scsi_mmc.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern int get_supported_cdrw_media_types(SCSI *usalp);
+#endif
+
+/*
+ * mmc_misc.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+extern int check_writemodes_mmc(SCSI *usalp, cdr_t *dp);
+#endif /* _SCG_SCSITRANSP_H */
+
+/*
+ * cdr_drv.c
+ */
+#ifdef _SCG_SCSITRANSP_H
+#ifdef _SCG_SCSIREG_H
+extern cdr_t *drive_identify(SCSI *usalp, cdr_t *, struct scsi_inquiry *ip);
+#else
+extern cdr_t *drive_identify(SCSI *usalp, cdr_t *, void *ip);
+#endif
+extern int drive_attach(SCSI *usalp, cdr_t *);
+#endif
+extern int attach_unknown(void);
+#ifdef _SCG_SCSITRANSP_H
+extern int blank_dummy(SCSI *usalp, cdr_t *, long addr, int blanktype);
+int format_dummy(SCSI *usalp, cdr_t *, int fmtflags);
+extern int drive_getdisktype(SCSI *usalp, cdr_t *dp);
+extern int cmd_ill(SCSI *usalp);
+extern int cmd_dummy(SCSI *usalp, cdr_t *);
+extern int no_sendcue(SCSI *usalp, cdr_t *, track_t *trackp);
+extern int buf_dummy(SCSI *usalp, long *sp, long *fp);
+#endif
+extern BOOL set_cdrcmds(char *name, cdr_t **dpp);
+#ifdef _SCG_SCSITRANSP_H
+extern cdr_t *get_cdrcmds(SCSI *usalp);
+#endif
+
+
+/*
+ * drv_mmc.c
+ */
+extern void mmc_opthelp(cdr_t *dp, int excode);
+extern char *hasdrvopt(char *optstr, char *optname);
+#ifdef _SCG_SCSITRANSP_H
+extern struct ricoh_mode_page_30 *get_justlink_ricoh(SCSI *usalp, Uchar *mode);
+#endif
+
+/*
+ * isosize.c
+ */
+extern Llong isosize(int f);
+
+/*
+ * audiosize.c
+ */
+extern BOOL is_auname(const char *name);
+extern off_t ausize(int f);
+extern BOOL is_wavname(const char *name);
+extern off_t wavsize(int f);
+
+/*
+ * auinfo.c
+ */
+extern BOOL auinfosize(char *name, track_t *trackp);
+extern void auinfo(char *name, int track, track_t *trackp);
+#ifdef CDTEXT_H
+extern textptr_t *gettextptr(int track, track_t *trackp);
+#endif
+extern void setmcn(char *mcn, track_t *trackp);
+extern void setisrc(char *isrc, track_t *trackp);
+extern void setindex(char *tindex, track_t *trackp);
+
+/*
+ * diskid.c
+ */
+extern void pr_manufacturer(msf_t *mp, BOOL rw, BOOL audio);
+extern int manufacturer_id(msf_t *mp);
+extern long disk_rcap(msf_t *mp, long maxblock, BOOL rw, BOOL audio);
+
+/*--------------------------------------------------------------------------*/
+/* Test only */
+/*--------------------------------------------------------------------------*/
+#ifdef _SCSIMMC_H
+/*extern int do_cue __PR((track_t *trackp, struct mmc_cue **cuep));*/
+#else
+/*extern int do_cue __PR((track_t *trackp, void *cuep));*/
+#endif
+
+/*
+ * subchan.c
+ */
+extern int do_leadin(track_t *trackp);
+#ifdef _SCG_SCSITRANSP_H
+extern int write_leadin(SCSI *usalp, cdr_t *dp, track_t *trackp,
+ int leadinstart);
+extern int write_leadout(SCSI *usalp, cdr_t *dp, track_t *trackp);
+#endif
+extern void fillsubch(track_t *trackp, Uchar *sp, int secno, int nsecs);
+extern void filltpoint(Uchar *sub, int ctrl_adr, int point, msf_t *mp);
+extern void fillttime(Uchar *sub, msf_t *mp);
+extern void qpto96(Uchar *sub, Uchar *subq, int dop);
+extern void addrw(Uchar *sub, Uchar *subrwptr);
+extern void qwto16(Uchar *subq, Uchar *subptr);
+extern void subrecodesecs(track_t *trackp, Uchar *bp, int address, int nsecs);
+
+/*
+ * sector.c
+ */
+extern int encspeed(BOOL be_verbose);
+extern void encsectors(track_t *trackp, Uchar *bp, int address, int nsecs);
+extern void scrsectors(track_t *trackp, Uchar *bp, int address, int nsecs);
+extern void encodesector(Uchar *sp, int sectype, int address);
+extern void fillsector(Uchar *sp, int sectype, int address);
+
+/*
+ * clone.c
+ */
+extern void clone_toc(track_t *trackp);
+extern void clone_tracktype(track_t *trackp);
+
+/*
+ * cdtext.c
+ */
+extern BOOL checktextfile(char *fname);
+extern void packtext(int tracks, track_t *trackp);
+#ifdef _SCG_SCSITRANSP_H
+extern int write_cdtext(SCSI *usalp, cdr_t *dp, long startsec);
+#endif
+
+/*
+ * cue.c
+ */
+extern int parsecue(char *cuefname, track_t trackp[]);
+#ifdef EOF /* stdio.h has been included */
+extern void fparsecue(FILE *f, track_t trackp[]);
+#endif
diff --git a/wodim/xio.c b/wodim/xio.c
new file mode 100644
index 0000000..04118cd
--- /dev/null
+++ b/wodim/xio.c
@@ -0,0 +1,164 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)xio.c 1.11 04/07/11 Copyright 2003-2004 J. Schilling */
+/*
+ * EXtended I/O functions for cdrecord
+ *
+ * Copyright (c) 2003-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <mconfig.h>
+#include <unixstd.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <standard.h>
+#include <fctldefs.h>
+
+#ifdef NEED_O_BINARY
+#include <io.h> /* for setmode() prototype */
+#endif
+
+#include "xio.h"
+
+static xio_t x_stdin = {
+ NULL, /* x_next */
+ NULL, /* x_name */
+ 0, /* x_off */
+ STDIN_FILENO, /* x_file */
+ 999, /* x_refcnt */
+ O_RDONLY, /* x_oflag */
+ 0 /* x_omode */
+};
+
+static xio_t *x_root = &x_stdin;
+static xio_t **x_tail = NULL;
+
+
+static xio_t *xnewnode(char *name);
+void *xopen(char *name, int oflag, int mode);
+int xclose(void *vp);
+
+static xio_t *
+xnewnode(char *name)
+{
+ xio_t *xp;
+
+ if ((xp = malloc(sizeof (xio_t))) == NULL)
+ return ((xio_t *) NULL);
+
+ xp->x_next = (xio_t *) NULL;
+ xp->x_name = strdup(name);
+ if (xp->x_name == NULL) {
+ free(xp);
+ return ((xio_t *) NULL);
+ }
+ xp->x_off = 0;
+ xp->x_file = -1;
+ xp->x_refcnt = 1;
+ xp->x_oflag = 0;
+ xp->x_omode = 0;
+ return (xp);
+}
+
+void *
+xopen(char *name, int oflag, int mode)
+{
+ int f;
+ xio_t *xp;
+ xio_t *pp = x_root;
+
+ if (x_tail == NULL)
+ x_tail = &x_stdin.x_next;
+ if (name == NULL) {
+ xp = &x_stdin;
+ xp->x_refcnt++;
+#ifdef NEED_O_BINARY
+ if ((oflag & O_BINARY) != 0) {
+ setmode(STDIN_FILENO, O_BINARY);
+ }
+#endif
+ return (xp);
+ }
+ for (; pp; pp = pp->x_next) {
+ if (pp->x_name == NULL) /* stdin avoid core dump in strcmp() */
+ continue;
+ if ((strcmp(pp->x_name, name) == 0) &&
+ (pp->x_oflag == oflag) && (pp->x_omode == mode)) {
+ break;
+ }
+ }
+ if (pp) {
+ pp->x_refcnt++;
+ return ((void *)pp);
+ }
+ if ((f = open(name, oflag, mode)) < 0)
+ return (NULL);
+
+ if ((xp = xnewnode(name)) == NULL) {
+ close(f);
+ return (NULL);
+ }
+ xp->x_file = f;
+ xp->x_oflag = oflag;
+ xp->x_omode = mode;
+ *x_tail = xp;
+ x_tail = &xp->x_next;
+ return ((void *)xp);
+}
+
+int
+xclose(void *vp)
+{
+ xio_t *xp = vp;
+ xio_t *pp = x_root;
+ int ret = 0;
+
+ if (xp == &x_stdin)
+ return (ret);
+ if (x_tail == NULL)
+ x_tail = &x_stdin.x_next;
+
+ if (--xp->x_refcnt <= 0) {
+ ret = close(xp->x_file);
+ while (pp) {
+ if (pp->x_next == xp)
+ break;
+ if (pp->x_next == NULL)
+ break;
+ pp = pp->x_next;
+ }
+ if (pp->x_next == xp) {
+ if (x_tail == &xp->x_next)
+ x_tail = &pp->x_next;
+ pp->x_next = xp->x_next;
+ }
+
+ free(xp->x_name);
+ free(xp);
+ }
+ return (ret);
+}
diff --git a/wodim/xio.h b/wodim/xio.h
new file mode 100644
index 0000000..a7836b2
--- /dev/null
+++ b/wodim/xio.h
@@ -0,0 +1,54 @@
+/*
+ * This file has been modified for the cdrkit suite.
+ *
+ * The behaviour and appearence of the program code below can differ to a major
+ * extent from the version distributed by the original author(s).
+ *
+ * For details, see Changelog file distributed with the cdrkit package. If you
+ * received this file from another source then ask the distributing person for
+ * a log of modifications.
+ *
+ */
+
+/* @(#)xio.h 1.2 04/03/02 Copyright 2003-2004 J. Schilling */
+/*
+ * EXtended I/O functions for cdrecord
+ *
+ * Copyright (c) 2003-2004 J. Schilling
+ */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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; see the file COPYING. If not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _XIO_H
+#define _XIO_H
+
+#include <utypes.h>
+
+typedef struct xio {
+ struct xio *x_next;
+ char *x_name;
+ Ullong x_off;
+ int x_file;
+ int x_refcnt;
+ int x_oflag;
+ int x_omode;
+} xio_t;
+
+#define xfileno(p) (((xio_t *)(p))->x_file)
+
+extern void *xopen(char *name, int oflag, int mode);
+extern int xclose(void *vp);
+
+#endif