diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2012-12-31 05:04:42 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2012-12-31 05:04:42 +0400 |
commit | 71dc8760ff4de5f365330d1bc571d934deb54af9 (patch) | |
tree | 7346d42a282562a3937d82307012b5857d642ce6 /wodim | |
download | cdrkit-71dc8760ff4de5f365330d1bc571d934deb54af9.tar.gz |
Imported Upstream version 1.1.11upstream/1.1.11upstream
Diffstat (limited to 'wodim')
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 = ¬able; + } + 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 == ¬able) { + 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, ©, + &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 *)¶m); + 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 |