cdrtools/cdda2wav/interface.c

1259 lines
31 KiB
C
Raw Normal View History

2025-06-15 04:19:58 +08:00
/* @(#)interface.c 1.83 16/02/14 Copyright 1998-2002,2015 Heiko Eissfeldt, Copyright 2006-2016 J. Schilling */
#include "config.h"
#ifndef lint
static UConst char sccsid[] =
"@(#)interface.c 1.83 16/02/14 Copyright 1998-2002,2015 Heiko Eissfeldt, Copyright 2006-2016 J. Schilling";
#endif
/*
* Copyright (C) 1998-2002,2015 Heiko Eissfeldt heiko@colossus.escape.de
* Copyright (c) 2006-2016 J. Schilling
*
* Interface module for cdrom drive access
*
* Two interfaces are possible.
*
* 1. using 'cooked' ioctls() (Linux only)
* : available for atapi, sbpcd and cdu31a drives only.
*
* 2. using the generic scsi device (for details see SCSI Prog. HOWTO).
* NOTE: a bug/misfeature in the kernel requires blocking signal
* SIGINT during SCSI command handling. Once this flaw has
* been removed, the sigprocmask SIG_BLOCK and SIG_UNBLOCK calls
* should removed, thus saving context switches.
*
* For testing purposes I have added a third simulation interface.
*
* Version 0.8: used experiences of Jochen Karrer.
* SparcLinux port fixes
* AlphaLinux port fixes
*
*/
/*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* See the file CDDL.Schily.txt in this distribution for details.
* A copy of the CDDL is also available via the Internet at
* http://www.opensource.org/licenses/cddl1.txt
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file CDDL.Schily.txt from this distribution.
*/
#if 0
#define SIM_CD
#endif
#include "config.h"
#include <schily/stdio.h>
#include <schily/standard.h>
#include <schily/stdlib.h>
#include <schily/unistd.h>
#include <schily/string.h>
#include <schily/errno.h>
#include <schily/signal.h>
#include <schily/fcntl.h>
#include <schily/assert.h>
#include <schily/nlsdefs.h>
#include <schily/device.h>
#include <schily/ioctl.h>
#include <schily/stat.h>
#include <schily/time.h>
#include <schily/poll.h>
#include <schily/schily.h>
#include "mycdrom.h"
#include "lowlevel.h"
/* some include file locations have changed with newer kernels */
#if defined(__linux__)
# if LINUX_VERSION_CODE > 0x10300 + 97
# if LINUX_VERSION_CODE < 0x200ff
# include <linux/sbpcd.h>
# include <linux/ucdrom.h>
# endif
# if !defined(CDROM_SELECT_SPEED)
# include <linux/ucdrom.h>
# endif
# endif
#endif
#include <scg/scsireg.h>
#include <scg/scsitransp.h>
#include "mytype.h"
#include "byteorder.h"
#include "interface.h"
#include "cdda2wav.h"
#include "semshm.h"
#include "setuid.h"
#include "ringbuff.h"
#include "toc.h"
#include "global.h"
#include "ioctl.h"
#include "exitcodes.h"
#include "scsi_cmds.h"
#include <schily/utypes.h>
#include <cdrecord.h>
#include "scsi_scan.h"
unsigned interface;
int trackindex_disp = 0;
void (*EnableCdda) __PR((SCSI *, int Switch, unsigned uSectorsize));
unsigned (*doReadToc) __PR((SCSI *scgp));
void (*ReadTocText) __PR((SCSI *scgp));
unsigned (*ReadLastAudio) __PR((SCSI *scgp));
int (*ReadCdRom) __PR((SCSI *scgp, UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
int (*ReadCdRom_C2) __PR((SCSI *scgp, UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
int (*ReadCdRomData) __PR((SCSI *scgp, unsigned char *p, unsigned lSector,
unsigned SectorBurstVal));
int (*ReadCdRomSub) __PR((SCSI *scgp, UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
subq_chnl *(*ReadSubChannels) __PR((SCSI *scgp, unsigned lSector));
subq_chnl *(*ReadSubQ) __PR((SCSI *scgp, unsigned char sq_format,
unsigned char track));
void (*SelectSpeed) __PR((SCSI *scgp, unsigned speed));
int (*Play_at) __PR((SCSI *scgp, unsigned int from_sector,
unsigned int sectors));
int (*StopPlay) __PR((SCSI *scgp));
void (*trash_cache) __PR((UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
#if defined USE_PARANOIA
long cdda_read __PR((void *d, void * buffer, long beginsector,
long sectors));
long cdda_read_c2 __PR((void *d, void * buffer, long beginsector,
long sectors));
long
cdda_read(d, buffer, beginsector, sectors)
void *d;
void *buffer;
long beginsector;
long sectors;
{
long ret = ReadCdRom(d, buffer, beginsector, sectors);
return (ret);
}
long
cdda_read_c2(d, buffer, beginsector, sectors)
void *d;
void *buffer;
long beginsector;
long sectors;
{
long ret = ReadCdRom_C2(d, buffer, beginsector, sectors);
return (ret);
}
#endif
typedef struct string_len {
char *str;
unsigned int sl;
} mystring;
static mystring drv_is_not_mmc[] = {
{ "DEC RRD47 (C) DEC ", 24},
/* { "SONY CD-ROM CDU625 1.0", 28}, */
{ NULL, 0} /* must be last entry */
};
static mystring drv_has_mmc_cdda[] = {
{ "HITACHI CDR-7930", 16 },
/* { "TOSHIBA CD-ROM XM-5402TA3605", 28}, */
{ NULL, 0} /* must be last entry */
};
static int Is_a_Toshiba3401;
int Toshiba3401 __PR((void));
int
Toshiba3401() {
return (Is_a_Toshiba3401);
}
/* hook */
static void Dummy __PR((void));
static void
Dummy()
{
}
static SCSI *_scgp;
SCSI *get_scsi_p __PR((void));
SCSI *
get_scsi_p()
{
return (_scgp);
}
#if !defined(SIM_CD)
static void trash_cache_SCSI __PR((UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
static void
trash_cache_SCSI(p, lSector, SectorBurstVal)
UINT4 *p;
unsigned lSector;
unsigned SectorBurstVal;
{
/*
* trash the cache
*/
ReadCdRom(get_scsi_p(), p,
find_an_off_sector(lSector, SectorBurstVal),
min(global.nsectors, 6));
}
static void Check_interface_for_device __PR((struct stat *statstruct,
char *pdev_name));
LOCAL int OpenCdRom __PR((char *pdev_name));
LOCAL void scg_openerr __PR((char *errstr));
LOCAL int find_drive __PR((SCSI *scgp, char *dev));
static void SetupSCSI __PR((SCSI *scgp));
static void
SetupSCSI(scgp)
SCSI *scgp;
{
unsigned char *p;
int err;
if (interface != GENERIC_SCSI) {
/*
* unfortunately we have the wrong interface and are
* not able to change on the fly
*/
errmsgno(EX_BAD,
_("The generic SCSI interface and devices are required\n"));
exit(SYNTAX_ERROR);
}
/*
* do a test unit ready to 'init' the device.
*/
seterrno(0);
TestForMedium(scgp);
err = geterrno();
if (err == EPERM || err == EACCES) {
scg_openerr("");
/* NOTREACHED */
}
/*
* check for the correct type of unit.
*/
p = ScsiInquiry(scgp);
#undef TYPE_ROM
#define TYPE_ROM 5
#undef TYPE_WORM
#define TYPE_WORM 4
if (p == NULL) {
errmsgno(EX_BAD, _("Inquiry command failed. Aborting...\n"));
exit(DEVICE_ERROR);
}
if ((*p != TYPE_ROM && *p != TYPE_WORM)) {
errmsgno(EX_BAD,
_("This is neither a scsi cdrom nor a worm device.\n"));
exit(SYNTAX_ERROR);
}
if (global.quiet == 0) {
fprintf(outfp,
_("Type: %s, Vendor '%8.8s' Model '%16.16s' Revision '%4.4s' "),
*p == TYPE_ROM ? "ROM" : "WORM",
p+8,
p+16,
p+32);
}
/*
* generic Sony type defaults
*/
density = 0x0;
accepts_fua_bit = -1;
EnableCdda = (void (*) __PR((SCSI *, int, unsigned)))Dummy;
ReadCdRom = ReadCdda12;
ReadCdRomSub = ReadCddaSubSony;
ReadCdRom_C2 = ReadCddaNoFallback_C2;
ReadCdRomData = (int (*) __PR((SCSI *,
unsigned char *,
unsigned, unsigned))) ReadStandardData;
ReadLastAudio = ReadFirstSessionTOCSony;
SelectSpeed = SpeedSelectSCSISony;
Play_at = Play_atSCSI;
StopPlay = StopPlaySCSI;
trash_cache = trash_cache_SCSI;
ReadTocText = ReadTocTextSCSIMMC;
doReadToc = ReadTocSCSI;
ReadSubQ = ReadSubQSCSI;
ReadSubChannels = (subq_chnl * (*) __PR((SCSI *, unsigned)))NULL;
/*
* check for brands and adjust special peculiaritites
*/
/*
* If your drive is not treated correctly, you can adjust some things
* here:
* global.in_lendian: should be to 1, if the CDROM drive or CD-Writer
* delivers the samples in the native byteorder of the audio cd
* (LSB first).
* HP CD-Writers need it set to 0.
* NOTE: If you get correct wav files when using sox with the '-x'
* option, the endianess is wrong. You can use the -C option to
* specify the value of global.in_lendian.
*/
{
int mmc_code;
scgp->silent++;
allow_atapi(scgp, 1);
if (*p == TYPE_ROM) {
mmc_code = heiko_mmc(scgp);
} else {
mmc_code = 0;
}
scgp->silent--;
/*
* Exceptions for drives that report incorrect MMC capability
*/
if (mmc_code != 0) {
/*
* these drives are NOT capable of MMC commands
*/
mystring *pp = drv_is_not_mmc;
while (pp->str != NULL) {
if (strncmp(pp->str, (char *)p+8, pp->sl) == 0) {
mmc_code = 0;
break;
}
pp++;
}
}
{
/*
* these drives flag themselves as non-MMC, but offer
* CDDA reading only with a MMC method.
*/
mystring *pp = drv_has_mmc_cdda;
while (pp->str != NULL) {
if (strncmp(pp->str, (char *)p+8, pp->sl) == 0) {
mmc_code = 1;
break;
}
pp++;
}
}
switch (mmc_code) {
case 2: /* SCSI-3 cdrom drive with accurate audio stream */
/* FALLTHROUGH */
case 1: /* SCSI-3 cdrom drive with no accurate audio stream */
/* FALLTHROUGH */
lost_toshibas:
global.in_lendian = 1;
if (mmc_code == 2)
global.overlap = 0;
else
global.overlap = 1;
ReadCdRom = ReadCddaFallbackMMC;
ReadCdRom_C2 = ReadCddaFallbackMMC_C2;
ReadCdRomSub = ReadCddaSubSony;
ReadLastAudio = ReadFirstSessionTOCMMC;
SelectSpeed = SpeedSelectSCSIMMC;
ReadTocText = ReadTocTextSCSIMMC;
doReadToc = ReadTocMMC;
ReadSubChannels = ReadSubChannelsFallbackMMC;
if (!memcmp(p+8, "SONY CD-RW CRX100E 1.0", 27))
ReadTocText = (void (*) __PR((SCSI *)))NULL;
if (!global.quiet)
fprintf(outfp, "MMC+CDDA\n");
break;
case -1: /* "MMC drive does not support cdda reading, sorry\n." */
doReadToc = ReadTocMMC;
if (!global.quiet)
fprintf(outfp, "MMC-CDDA\n");
/* FALLTHROUGH */
case 0: /* non SCSI-3 cdrom drive */
if (!global.quiet) fprintf(outfp, _("no MMC\n"));
ReadLastAudio = (unsigned (*) __PR((SCSI *)))NULL;
if (!memcmp(p+8, "TOSHIBA", 7) ||
!memcmp(p+8, "IBM", 3) ||
!memcmp(p+8, "DEC", 3)) {
/*
* Older Toshiba ATAPI drives don't identify
* themselves as MMC.
* The last digit of the model number is
* '2' for ATAPI drives.
* These are treated as MMC.
*/
if (!memcmp(p+15, " CD-ROM XM-", 11) &&
p[29] == '2') {
goto lost_toshibas;
}
density = 0x82;
EnableCdda = EnableCddaModeSelect;
ReadSubChannels = ReadStandardSub;
ReadCdRom = ReadStandard;
SelectSpeed = SpeedSelectSCSIToshiba;
if (!memcmp(p+15, " CD-ROM XM-3401", 15)) {
Is_a_Toshiba3401 = 1;
}
global.in_lendian = 1;
} else if (!memcmp(p+8, "IMS", 3) ||
!memcmp(p+8, "KODAK", 5) ||
!memcmp(p+8, "RICOH", 5) ||
!memcmp(p+8, "HP", 2) ||
!memcmp(p+8, "PHILIPS", 7) ||
!memcmp(p+8, "PLASMON", 7) ||
!memcmp(p+8, "GRUNDIG CDR100IPW", 17) ||
!memcmp(p+8, "MITSUMI CD-R ", 13)) {
EnableCdda = EnableCddaModeSelect;
ReadCdRom = ReadStandard;
SelectSpeed = SpeedSelectSCSIPhilipsCDD2600;
/*
* treat all of these as bigendian
*/
global.in_lendian = 0;
/*
* no overlap reading for cd-writers
*/
global.overlap = 0;
} else if (!memcmp(p+8, "NRC", 3)) {
SelectSpeed = (void (*) __PR((SCSI *, unsigned)))NULL;
} else if (!memcmp(p+8, "YAMAHA", 6)) {
EnableCdda = EnableCddaModeSelect;
SelectSpeed = SpeedSelectSCSIYamaha;
/*
* no overlap reading for cd-writers
*/
global.overlap = 0;
global.in_lendian = 1;
} else if (!memcmp(p+8, "PLEXTOR", 7)) {
global.in_lendian = 1;
global.overlap = 0;
ReadLastAudio = ReadFirstSessionTOCSony;
ReadTocText = ReadTocTextSCSIMMC;
doReadToc = ReadTocSony;
ReadSubChannels = ReadSubChannelsSony;
} else if (!memcmp(p+8, "SONY", 4)) {
global.in_lendian = 1;
if (!memcmp(p+16, "CD-ROM CDU55E", 13)) {
ReadCdRom = ReadCddaMMC12;
}
ReadLastAudio = ReadFirstSessionTOCSony;
ReadTocText = ReadTocTextSCSIMMC;
doReadToc = ReadTocSony;
ReadSubChannels = ReadSubChannelsSony;
} else if (!memcmp(p+8, "NEC", 3)) {
ReadCdRom = ReadCdda10;
ReadTocText = (void (*) __PR((SCSI *)))NULL;
SelectSpeed = SpeedSelectSCSINEC;
global.in_lendian = 1;
/*
* I assume all versions of the 502 require
* this?
* no overlap reading for NEC CD-ROM 502!
*/
if (!memcmp(p+29, "5022.0r", 3))
global.overlap = 0;
} else if (!memcmp(p+8, "MATSHITA", 8)) {
ReadCdRom = ReadCdda12Matsushita;
global.in_lendian = 1;
}
} /* switch (get_mmc) */
}
/*
* look if caddy is loaded
*/
if (interface == GENERIC_SCSI) {
scgp->silent++;
while (!wait_unit_ready(scgp, 60)) {
int c;
fprintf(outfp,
_("load cdrom please and press enter"));
fflush(outfp);
while ((c = getchar()) != '\n') {
if (c == EOF)
break;
}
if (c == EOF)
exit(DEVICE_ERROR);
}
scgp->silent--;
}
}
/********************** General setup *******************************/
/*
* As the name implies, interfaces and devices are checked. We also
* adjust nsectors, overlap, and interface for the first time here.
* Any unnecessary privileges (setuid, setgid) are also dropped here.
*/
static void
Check_interface_for_device(statstruct, pdev_name)
struct stat *statstruct;
char *pdev_name;
{
#if !defined(STAT_MACROS_BROKEN) || (STAT_MACROS_BROKEN != 1)
if (!S_ISCHR(statstruct->st_mode) &&
!S_ISBLK(statstruct->st_mode)) {
errmsgno(EX_BAD, _("%s is not a device.\n"),
pdev_name);
exit(SYNTAX_ERROR);
}
#endif
#if defined(HAVE_ST_RDEV) && (HAVE_ST_RDEV == 1)
switch (major(statstruct->st_rdev)) {
#if defined(__linux__)
case SCSI_GENERIC_MAJOR: /* generic */
#else
default: /* ??? what is the proper value here */
#endif
#if !defined(STAT_MACROS_BROKEN) || (STAT_MACROS_BROKEN != 1)
#if defined(__linux__)
if (!S_ISCHR(statstruct->st_mode)) {
errmsgno(EX_BAD, _("%s is not a char device.\n"),
pdev_name);
exit(SYNTAX_ERROR);
}
if (interface != GENERIC_SCSI) {
fprintf(outfp,
_("wrong interface (cooked_ioctl) for this device (%s)\nset to generic_scsi\n"),
pdev_name);
interface = GENERIC_SCSI;
}
#endif
#else
default: /* ??? what is the proper value here */
#endif
break;
#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#if defined(__linux__)
case SCSI_CDROM_MAJOR: /* scsi cd */
default: /* for example ATAPI cds */
#else
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
#if __FreeBSD_version >= 600021
case 0: /* majors abandoned */
/* FALLTHROUGH */
#endif
#if __FreeBSD_version >= 501113
case 4: /* GEOM */
/* FALLTHROUGH */
#endif
case 117: /* pre-GEOM atapi cd */
if (!S_ISCHR(statstruct->st_mode)) {
errmsgno(EX_BAD, _("%s is not a char device.\n"),
pdev_name);
exit(SYNTAX_ERROR);
}
if (interface != COOKED_IOCTL) {
fprintf(outfp,
_("cdrom device (%s) is not of type generic SCSI. \
Setting interface to cooked_ioctl.\n"), pdev_name);
interface = COOKED_IOCTL;
}
break;
case 19: /* first atapi cd */
#endif
#endif
if (!S_ISBLK(statstruct->st_mode)) {
errmsgno(EX_BAD, _("%s is not a block device.\n"),
pdev_name);
exit(SYNTAX_ERROR);
}
#if defined(__linux__)
#if LINUX_VERSION_CODE >= 0x20600
/*
* In Linux kernel 2.6 it is better to use the SCSI interface
* with the device.
*/
break;
#endif
#endif
if (interface != COOKED_IOCTL) {
fprintf(outfp,
_("cdrom device (%s) is not of type generic SCSI. \
Setting interface to cooked_ioctl.\n"), pdev_name);
interface = COOKED_IOCTL;
}
if (interface == COOKED_IOCTL) {
fprintf(outfp,
_("\nW: The cooked_ioctl interface is functionally very limited!!\n"));
#if defined(__linux__)
fprintf(outfp,
_("\nW: For good sampling quality simply use the generic SCSI interface!\n"
"For example dev=1,0,0\n"));
#endif
}
break;
#endif
}
#endif
if (global.overlap >= global.nsectors)
global.overlap = global.nsectors-1;
}
/*
* open the cdrom device
*/
static int
OpenCdRom(pdev_name)
char *pdev_name;
{
int retval = 0;
struct stat fstatstruct;
#ifdef HAVE_IOCTL_INTERFACE
struct stat statstruct;
int have_named_device = 0;
#endif
interface = GENERIC_SCSI;
/*
* The device (given by pdevname) can be:
* a. an SCSI device specified with a /dev/xxx name,
* b. an SCSI device specified with bus,target,lun numbers,
* c. a non-SCSI device such as ATAPI or proprietary CDROM devices.
*/
#ifdef HAVE_IOCTL_INTERFACE
have_named_device = FALSE;
if (pdev_name) {
have_named_device = strchr(pdev_name, ':') == NULL &&
memcmp(pdev_name, "/dev/", 5) == 0;
} else {
interface = GENERIC_SCSI; /* Paranoia for "coverity" */
}
if (have_named_device) {
if (stat(pdev_name, &statstruct)) {
errmsg(_("Cannot stat device %s.\n"), pdev_name);
exit(STAT_ERROR);
} else {
Check_interface_for_device(&statstruct, pdev_name);
}
}
#endif
if (interface == GENERIC_SCSI) {
SCSI *scgp;
char errstr[80];
needroot(0);
needgroup(0);
if (global.issetuid || global.uid != 0)
priv_on();
/*
* Call scg_remote() to force loading the remote SCSI transport
* library code that is located in librscg instead of the dummy
* remote routines that are located inside libscg.
*/
scg_remote();
if (pdev_name != NULL &&
((strncmp(pdev_name, "HELP", 4) == 0) ||
(strncmp(pdev_name, "help", 4) == 0))) {
scg_help(stderr);
exit(NO_ERROR);
}
/*
* device name, debug, verboseopen
*/
_scgp = scg_open(pdev_name, errstr, sizeof (errstr), 0, 0);
if (_scgp == NULL) {
scg_openerr(errstr);
/* NOTREACHED */
}
scgp = _scgp;
scg_settimeout(scgp, 300);
scg_settimeout(scgp, 60);
if (global.dev_opts) {
int i = scg_opts(scgp, global.dev_opts);
if (i <= 0)
exit(i < 0 ? EX_BAD : 0);
}
scgp->silent = global.scsi_silent;
scgp->verbose = global.scsi_verbose;
scgp->debug = global.scsi_debug;
scgp->kdebug = global.scsi_kdebug;
global.bufsize = scg_bufsize(scgp, global.bufsize);
if (global.nsectors >
(unsigned)global.bufsize/CD_FRAMESIZE_RAW) {
global.nsectors = global.bufsize/CD_FRAMESIZE_RAW;
}
#ifdef USE_PARANOIA
if (global.paranoia_parms.enable_c2_check && global.nsectors >
(unsigned)global.bufsize/CD_FRAMESIZE_RAWER) {
global.nsectors = global.bufsize/CD_FRAMESIZE_RAWER;
}
#endif
if (global.overlap >= global.nsectors)
global.overlap = global.nsectors-1;
/*
* Newer versions of Linux seem to introduce an incompatible
* change and require root privileges or limit RLIMIT_MEMLOCK
* infinity in order to get a SCSI buffer in case we did call
* mlockall(MCL_FUTURE).
*/
init_scsibuf(scgp, global.bufsize);
if (global.issetuid || global.uid != 0)
priv_off();
dontneedgroup();
dontneedroot();
if (global.scanbus) {
int i = select_target(scgp, outfp);
if (i < 0) {
scg_openerr("");
/* NOTREACHED */
}
exit(0);
}
if (scg_scsibus(scgp) < 0 &&
scg_target(scgp) < 0 && scg_lun(scgp) < 0) {
int i = find_drive(scgp, pdev_name);
if (i < 0) {
scg_openerr("");
/* NOTREACHED */
}
}
} else {
needgroup(0);
retval = open(pdev_name,
#ifdef linux
O_NONBLOCK |
#endif
O_RDONLY);
dontneedgroup();
if (retval < 0) {
errmsg(_("Cannot open '%s'.\n"), pdev_name);
exit(DEVICEOPEN_ERROR);
}
/*
* Do final security checks here
*/
if (fstat(retval, &fstatstruct)) {
errmsg(_("Could not fstat %s (fd %d).\n"),
pdev_name, retval);
exit(STAT_ERROR);
}
Check_interface_for_device(&fstatstruct, pdev_name);
#if defined HAVE_IOCTL_INTERFACE
/* Watch for race conditions */
if (have_named_device &&
(fstatstruct.st_dev != statstruct.st_dev ||
fstatstruct.st_ino != statstruct.st_ino)) {
errmsgno(EX_BAD,
_("Race condition attempted in OpenCdRom. Exiting now.\n"));
exit(RACE_ERROR);
}
#endif
/*
* The program structure looks like a desaster :-(
* We do this more than once as it is impossible to understand
* where the right place would be to do this....
*/
if (_scgp != NULL) {
_scgp->verbose = global.scsi_verbose;
}
}
return (retval);
}
LOCAL void
scg_openerr(errstr)
char *errstr;
{
int err = geterrno();
errmsgno(err, _("%s%sCannot open or use SCSI driver.\n"),
errstr, errstr[0]?". ":"");
errmsgno(EX_BAD,
_("For possible targets try 'cdda2wav -scanbus'.%s\n"),
geteuid() ?
_(" Make sure you are root."):"");
if (global.issetuid || global.uid != 0)
priv_off();
dontneedgroup();
dontneedroot();
#if defined(sun) || defined(__sun)
fprintf(stderr,
_("On SunOS/Solaris make sure you have Joerg Schillings scg SCSI driver installed.\n"));
#endif
#if defined(__linux__)
fprintf(stderr,
_("Use the script scan_scsi.linux to find out more.\n"));
#endif
fprintf(stderr,
_("Probably you did not define your SCSI device.\n"));
fprintf(stderr,
_("Set the CDDA_DEVICE environment variable or use the -D option.\n"));
fprintf(stderr,
_("You can also define the default device in the Makefile.\n"));
fprintf(stderr,
_("For possible transport specifiers try 'cdda2wav dev=help'.\n"));
exit(SYNTAX_ERROR);
}
LOCAL int
find_drive(scgp, dev)
SCSI *scgp;
char *dev;
{
int ntarget;
fprintf(outfp, _("No target specified, trying to find one...\n"));
ntarget = find_target(scgp, INQ_ROMD, -1);
if (ntarget < 0)
return (ntarget);
if (ntarget == 1) {
/*
* Simple case, exactly one CD-ROM found.
*/
find_target(scgp, INQ_ROMD, 1);
} else if (ntarget <= 0 &&
(ntarget = find_target(scgp, INQ_WORM, -1)) == 1) {
/*
* Exactly one CD-ROM acting as WORM found.
*/
find_target(scgp, INQ_WORM, 1);
} else if (ntarget <= 0) {
/*
* No single CD-ROM or WORM found.
*/
errmsgno(EX_BAD, _("No CD/DVD/BD-Recorder target found.\n"));
errmsgno(EX_BAD,
_("Your platform may not allow to scan for SCSI devices.\n"));
comerrno(EX_BAD,
_("Call 'cdda2wav dev=help' or ask your sysadmin for possible targets.\n"));
} else {
errmsgno(EX_BAD, _("Too many CD/DVD/BD-Recorder targets found.\n"));
select_target(scgp, outfp);
comerrno(EX_BAD,
_("Select a target from the list above and use 'cdda2wav dev=%s%sb,t,l'.\n"),
dev?dev:"", dev?(dev[strlen(dev)-1] == ':'?"":":"):"");
}
fprintf(outfp, _("Using dev=%s%s%d,%d,%d.\n"),
dev?dev:"", dev?(dev[strlen(dev)-1] == ':'?"":":"):"",
scg_scsibus(scgp), scg_target(scgp), scg_lun(scgp));
return (ntarget);
}
#endif /* SIM_CD */
/******************* Simulation interface *****************/
#if defined SIM_CD
#include "toc.h"
static unsigned long sim_pos = 0;
/*
* read 'SectorBurst' adjacent sectors of audio sectors
* to Buffer '*p' beginning at sector 'lSector'
*/
static int ReadCdRom_sim __PR((SCSI *x, UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
static int
ReadCdRom_sim(x, p, lSector, SectorBurstVal)
SCSI *x;
UINT4 *p;
unsigned lSector;
unsigned SectorBurstVal;
{
unsigned int loop = 0;
Int16_t *q = (Int16_t *) p;
int joffset = 0;
if (lSector > g_toc[cdtracks].dwStartSector ||
lSector + SectorBurstVal > g_toc[cdtracks].dwStartSector + 1) {
fprintf(stderr,
_("Read request out of bounds: %u - %u (%d - %d allowed)\n"),
lSector, lSector + SectorBurstVal,
0, g_toc[cdtracks].dwStartSector);
}
#if 0
/*
* jitter with a probability of jprob
*/
if (random() <= jprob) {
/*
* jitter up to jmax samples
*/
joffset = random();
}
#endif
#ifdef DEBUG_SHM
fprintf(stderr, ", last_b = %p\n", *last_buffer);
#endif
for (loop = lSector*CD_FRAMESAMPLES + joffset;
loop < (lSector+SectorBurstVal)*CD_FRAMESAMPLES + joffset;
loop++) {
*q++ = loop;
*q++ = ~loop;
}
#ifdef DEBUG_SHM
fprintf(stderr,
"sim wrote from %p upto %p - 4 (%d), last_b = %p\n",
p, q, SectorBurstVal*CD_FRAMESAMPLES, *last_buffer);
#endif
sim_pos = (lSector+SectorBurstVal)*CD_FRAMESAMPLES + joffset;
return (SectorBurstVal);
}
static int Play_at_sim __PR((SCSI *x, unsigned int from_sector,
unsigned int sectors));
static int
Play_at_sim(x, from_sector, sectors)
SCSI *x;
unsigned int from_sector;
unsigned int sectors;
{
sim_pos = from_sector*CD_FRAMESAMPLES;
return (0);
}
static unsigned sim_indices;
/*
* read the table of contents (toc) via the ioctl interface
*/
static unsigned ReadToc_sim __PR((SCSI *x, TOC *toc));
static unsigned
ReadToc_sim(x, toc)
SCSI *x;
TOC *toc;
{
unsigned int scenario;
int scen[12][3] = {
{ 1, 1, 500 },
{ 1, 2, 500 },
{ 1, 99, 150*99 },
{ 2, 1, 500 },
{ 2, 2, 500 },
{ 2, 99, 150*99 },
{ 2, 1, 500 },
{ 5, 2, 500 },
{ 5, 99, 150*99 },
{ 99, 1, 1000 },
{ 99, 2, 1000 },
{ 99, 99, 150*99 },
};
unsigned int i;
unsigned trcks;
#if 0
fprintf(stderr, "select one of the following TOCs\n"
"0 : 1 track with 1 index\n"
"1 : 1 track with 2 indices\n"
"2 : 1 track with 99 indices\n"
"3 : 2 tracks with 1 index each\n"
"4 : 2 tracks with 2 indices each\n"
"5 : 2 tracks with 99 indices each\n"
"6 : 2 tracks (data and audio) with 1 index each\n"
"7 : 5 tracks with 2 indices each\n"
"8 : 5 tracks with 99 indices each\n"
"9 : 99 tracks with 1 index each\n"
"10: 99 tracks with 2 indices each\n"
"11: 99 tracks with 99 indices each\n");
do {
scanf("%u", &scenario);
} while (scenario > sizeof (scen)/2/sizeof (int));
#else
scenario = 6;
#endif
/*
* build table of contents
*/
#if 0
trcks = scen[scenario][0] + 1;
sim_indices = scen[scenario][1];
for (i = 0; i < trcks; i++) {
toc[i].bFlags = (scenario == 6 && i == 0) ? 0x40 : 0xb1;
toc[i].bTrack = i + 1;
toc[i].dwStartSector = i * scen[scenario][2];
toc[i].mins = (toc[i].dwStartSector+150) / (60*75);
toc[i].secs = (toc[i].dwStartSector+150 / 75) % (60);
toc[i].frms = (toc[i].dwStartSector+150) % (75);
}
toc[i].bTrack = 0xaa;
toc[i].dwStartSector = i * scen[scenario][2];
toc[i].mins = (toc[i].dwStartSector+150) / (60*75);
toc[i].secs = (toc[i].dwStartSector+150 / 75) % (60);
toc[i].frms = (toc[i].dwStartSector+150) % (75);
#else
{
int starts[15] = { 23625, 30115, 39050, 51777, 67507,
88612, 112962, 116840, 143387, 162662,
173990, 186427, 188077, 209757, 257120};
trcks = 14 + 1;
sim_indices = 1;
for (i = 0; i < trcks; i++) {
toc[i].bFlags = 0x0;
toc[i].bTrack = i + 1;
toc[i].dwStartSector = starts[i];
toc[i].mins = (starts[i]+150) / (60*75);
toc[i].secs = (starts[i]+150 / 75) % (60);
toc[i].frms = (starts[i]+150) % (75);
}
toc[i].bTrack = 0xaa;
toc[i].dwStartSector = starts[i];
toc[i].mins = (starts[i]) / (60*75);
toc[i].secs = (starts[i] / 75) % (60);
toc[i].frms = (starts[i]) % (75);
}
#endif
return (--trcks); /* without lead-out */
}
static subq_chnl *ReadSubQ_sim __PR((SCSI *scgp,
unsigned char sq_format,
unsigned char track));
/*
* request sub-q-channel information. This function may cause confusion
* for a drive, when called in the sampling process.
*/
static subq_chnl *
ReadSubQ_sim(scgp, sq_format, track)
SCSI *scgp;
unsigned char sq_format;
unsigned char track;
{
subq_chnl *SQp = (subq_chnl *) (SubQbuffer);
subq_position *SQPp = (subq_position *) &SQp->data;
unsigned long sim_pos1;
unsigned long sim_pos2;
if (sq_format != GET_POSITIONDATA)
return (NULL); /* not supported by sim */
/*
* simulate CDROMSUBCHNL ioctl
*
* copy to SubQbuffer
*/
SQp->audio_status = 0;
SQp->format = 0xff;
SQp->control_adr = 0xff;
sim_pos1 = sim_pos/CD_FRAMESAMPLES;
sim_pos2 = sim_pos1 % 150;
SQp->track = (sim_pos1 / 5000) + 1;
SQp->index = ((sim_pos1 / 150) % sim_indices) + 1;
sim_pos1 += 150;
SQPp->abs_min = sim_pos1 / (75*60);
SQPp->abs_sec = (sim_pos1 / 75) % 60;
SQPp->abs_frame = sim_pos1 % 75;
SQPp->trel_min = sim_pos2 / (75*60);
SQPp->trel_sec = (sim_pos2 / 75) % 60;
SQPp->trel_frameb = sim_pos2 % 75;
return ((subq_chnl *)(SubQbuffer));
}
static void SelectSpeed_sim __PR((SCSI *x, unsigned sp));
/* ARGSUSED */
static void
SelectSpeed_sim(x, sp)
SCSI *x;
unsigned sp;
{
}
static void trash_cache_sim __PR((UINT4 *p, unsigned lSector,
unsigned SectorBurstVal));
/* ARGSUSED */
static void
trash_cache_sim(p, lSector, SectorBurstVal)
UINT4 *p;
unsigned lSector;
unsigned SectorBurstVal;
{
}
static void SetupSimCd __PR((void));
static void
SetupSimCd()
{
EnableCdda = (void (*) __PR((SCSI *, int, unsigned)))Dummy;
ReadCdRom = ReadCdRom_sim;
ReadCdRomData = (int (*) __PR((SCSI *,
unsigned char *,
unsigned, unsigned)))ReadCdRom_sim;
doReadToc = ReadToc_sim;
ReadTocText = (void (*) __PR((SCSI *)))NULL;
ReadSubQ = ReadSubQ_sim;
ReadSubChannels = (subq_chnl * (*) __PR((SCSI *, unsigned)))NULL;
ReadLastAudio = (unsigned (*) __PR((SCSI *)))NULL;
SelectSpeed = SelectSpeed_sim;
Play_at = Play_at_sim;
StopPlay = (int (*) __PR((SCSI *)))Dummy;
trash_cache = trash_cache_sim;
}
#endif /* def SIM_CD */
/* perform initialization depending on the interface used. */
void
SetupInterface()
{
#if defined SIM_CD
fprintf(stderr, "SIMULATION MODE !!!!!!!!!!!\n");
#else
/*
* ensure interface is setup correctly
*/
global.cooked_fd = OpenCdRom(global.dev_name);
#endif
#ifdef _SC_PAGESIZE
global.pagesize = sysconf(_SC_PAGESIZE);
#else
global.pagesize = getpagesize();
#endif
/*
* request one sector for table of contents
*/
bufTOCsize = CD_FRAMESIZE_RAW + 96; /* Sufficient space for 222 TOC entries */
bufferTOC = malloc(bufTOCsize); /* assumes sufficient aligned addresses */
/*
* SubQchannel buffer
*/
SubQbuffer = malloc(48); /* assumes sufficient aligned addresses */
if (!bufferTOC || !SubQbuffer) {
errmsg(_("Too low on memory. Giving up.\n"));
exit(NOMEM_ERROR);
}
#if defined SIM_CD
scgp = malloc(sizeof (* scgp));
if (scgp == NULL) {
FatalError(geterrno(), _("No memory for SCSI structure.\n"));
}
scgp->silent = 0;
SetupSimCd();
#else
/*
* if drive is of type scsi, get vendor name
*/
if (interface == GENERIC_SCSI) {
unsigned sector_size;
SCSI *scgp = _scgp;
SetupSCSI(scgp);
sector_size = get_orig_sectorsize(scgp, &orgmode4, &orgmode10,
&orgmode11);
if (!SCSI_emulated_ATAPI_on(scgp)) {
if (sector_size != 2048 &&
set_sectorsize(scgp, 2048)) {
fprintf(stderr,
_("Could not change sector size from %u to 2048\n"),
sector_size);
}
}
/*
* get cache setting
*
* set cache to zero
*/
} else {
#if defined(HAVE_IOCTL_INTERFACE)
_scgp = malloc(sizeof (* _scgp));
if (_scgp == NULL) {
FatalError(geterrno(),
_("No memory for SCSI structure.\n"));
}
_scgp->silent = 0;
SetupCookedIoctl(global.dev_name);
#else
FatalError(EX_BAD,
_("Sorry, there is no known method to access the device.\n"));
#endif
}
#endif /* if def SIM_CD */
/*
* The structure looks like a desaster :-(
* We do this more than once as it is impossible to understand where
* the right place would be to do this....
*/
if (_scgp != NULL) {
_scgp->verbose = global.scsi_verbose;
}
}
EXPORT int
poll_in()
{
#ifdef HAVE_POLL
struct pollfd pfd[1];
pfd[0].fd = fileno(stdin);
pfd[0].events = POLLIN;
pfd[0].revents = 0;
return (poll(pfd, 1, 0));
#else
#ifdef HAVE_SELECT
struct timeval tv;
fd_set rd;
FD_ZERO(&rd);
FD_SET(fileno(stdin), &rd);
tv.tv_sec = 0;
tv.tv_usec = 0;
return (select(1, &rd, NULL, NULL, &tv));
#else
comerrno(EX_BAD, _("Poll/Select not available.\n"));
#endif
#endif
}