summaryrefslogtreecommitdiff
path: root/icedax/interface.c
diff options
context:
space:
mode:
Diffstat (limited to 'icedax/interface.c')
-rw-r--r--icedax/interface.c1050
1 files changed, 1050 insertions, 0 deletions
diff --git a/icedax/interface.c b/icedax/interface.c
new file mode 100644
index 0000000..ef7278a
--- /dev/null
+++ b/icedax/interface.c
@@ -0,0 +1,1050 @@
+/*
+ * 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.
+ *
+ */
+
+/* @(#)interface.c 1.40 06/02/19 Copyright 1998-2002 Heiko Eissfeldt, Copyright 2006 J. Schilling */
+/***
+ * CopyPolicy: GNU Public License 2 applies
+ * Copyright (C) 1994-1997 Heiko Eissfeldt heiko@colossus.escape.de
+ *
+ * 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
+ *
+ */
+#if 0
+#define SIM_CD
+#endif
+
+#include "config.h"
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <unixstd.h>
+#include <strdefs.h>
+#include <errno.h>
+#include <signal.h>
+#include <fctldefs.h>
+#include <assert.h>
+#include <schily.h>
+#include <device.h>
+
+#include <sys/ioctl.h>
+#include <statdefs.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 <usal/scsitransp.h>
+
+#include "mytype.h"
+#include "byteorder.h"
+#include "interface.h"
+#include "icedax.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 <utypes.h>
+#include <wodim.h>
+#include "scsi_scan.h"
+
+unsigned interface;
+
+int trackindex_disp = 0;
+
+void priv_init(void);
+void priv_on(void);
+void priv_off(void);
+
+void (*EnableCdda)(SCSI *, int Switch, unsigned uSectorsize);
+unsigned (*doReadToc)(SCSI *usalp);
+void (*ReadTocText)(SCSI *usalp);
+unsigned (*ReadLastAudio)(SCSI *usalp);
+int (*ReadCdRom)(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+int (*ReadCdRomData)(SCSI *usalp, unsigned char *p, unsigned lSector,
+ unsigned SectorBurstVal);
+int (*ReadCdRomSub)(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+subq_chnl *(*ReadSubChannels)(SCSI *usalp, unsigned lSector);
+subq_chnl *(*ReadSubQ)(SCSI *usalp, unsigned char sq_format,
+ unsigned char track);
+void (*SelectSpeed)(SCSI *usalp, unsigned speed);
+int (*Play_at)(SCSI *usalp, unsigned int from_sector, unsigned int sectors);
+int (*StopPlay)(SCSI *usalp);
+void (*trash_cache)(UINT4 *p, unsigned lSector, unsigned SectorBurstVal);
+
+#if defined USE_PARANOIA
+long cdda_read(void *d, void *buffer, long beginsector, long sectors);
+
+long cdda_read(void *d, void *buffer, long beginsector, long sectors)
+{
+ long ret = ReadCdRom(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(void);
+
+int Toshiba3401()
+{
+ return Is_a_Toshiba3401;
+}
+
+/* hook */
+static void Dummy(void);
+static void Dummy()
+{
+}
+
+static SCSI *usalp;
+
+SCSI *get_scsi_p(void);
+
+SCSI *get_scsi_p()
+{
+ return usalp;
+}
+
+#if !defined(SIM_CD)
+
+static void trash_cache_SCSI(UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+
+static void trash_cache_SCSI(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(struct stat *statstruct,
+ char *pdev_name);
+static int OpenCdRom(char *pdev_name);
+
+static void SetupSCSI(void);
+
+static void SetupSCSI()
+{
+ unsigned char *p;
+
+ if (interface != GENERIC_SCSI) {
+ /* unfortunately we have the wrong interface and are
+ * not able to change on the fly */
+ fprintf(stderr, "The generic SCSI interface and devices are required\n");
+ exit(SYNTAX_ERROR);
+ }
+
+ /* do a test unit ready to 'init' the device. */
+ TestForMedium(usalp);
+
+ /* check for the correct type of unit. */
+ p = Inquiry(usalp);
+
+#undef TYPE_ROM
+#define TYPE_ROM 5
+#undef TYPE_WORM
+#define TYPE_WORM 4
+ if (p == NULL) {
+ fprintf(stderr, "Inquiry command failed. Aborting...\n");
+ exit(DEVICE_ERROR);
+ }
+
+ if ((*p != TYPE_ROM && *p != TYPE_WORM)) {
+ fprintf(stderr, "this is neither a scsi cdrom nor a worm device\n");
+ exit(SYNTAX_ERROR);
+ }
+
+ if (global.quiet == 0) {
+ fprintf(stderr,
+ "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 (*)(SCSI *, int, unsigned))Dummy;
+ ReadCdRom = ReadCdda12;
+ ReadCdRomSub = ReadCddaSubSony;
+ ReadCdRomData = (int (*)(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 = 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;
+
+ usalp->silent ++;
+ allow_atapi(usalp, 1);
+ if (*p == TYPE_ROM) {
+ mmc_code = heiko_mmc(usalp);
+ } else {
+ mmc_code = 0;
+ }
+ usalp->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)) {
+ 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)) {
+ mmc_code = 1;
+ break;
+ }
+ pp++;
+ }
+ }
+
+ switch (mmc_code) {
+ case 2: /* SCSI-3 cdrom drive with accurate audio stream */
+ /* fall through */
+ case 1: /* SCSI-3 cdrom drive with no accurate audio stream */
+ /* fall through */
+lost_toshibas:
+ global.in_lendian = 1;
+ if (mmc_code == 2)
+ global.overlap = 0;
+ else
+ global.overlap = 1;
+ ReadCdRom = ReadCddaFallbackMMC;
+ ReadCdRomSub = ReadCddaSubSony;
+ ReadLastAudio = ReadFirstSessionTOCMMC;
+ SelectSpeed = SpeedSelectSCSIMMC;
+ ReadTocText = ReadTocTextSCSIMMC;
+ doReadToc = ReadTocMMC;
+ ReadSubChannels = ReadSubChannelsFallbackMMC;
+ if (!memcmp(p+8,"SONY CD-RW CRX100E 1.0", 27)) ReadTocText = NULL;
+ if (!global.quiet) fprintf(stderr, "MMC+CDDA\n");
+ break;
+ case -1: /* "MMC drive does not support cdda reading, sorry\n." */
+ doReadToc = ReadTocMMC;
+ if (!global.quiet) fprintf(stderr, "MMC-CDDA\n");
+ /* FALLTHROUGH */
+ case 0: /* non SCSI-3 cdrom drive */
+ if (!global.quiet) fprintf(stderr, "no MMC\n");
+ ReadLastAudio = 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 = 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 = NULL;
+ SelectSpeed = SpeedSelectSCSINEC;
+ global.in_lendian = 1;
+ if (!memcmp(p+29,"5022.0r",3)) /* I assume all versions of the 502 require this? */
+ global.overlap = 0; /* no overlap reading for NEC CD-ROM 502 */
+ } 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) {
+ usalp->silent++;
+ while (!wait_unit_ready(usalp, 60)) {
+ fprintf(stderr,"load cdrom please and press enter");
+ getchar();
+ }
+ usalp->silent--;
+ }
+}
+
+/* Check to see if the device will support SCSI generic commands. A
+ * better check than simply looking at the device name. Open the
+ * device, issue an inquiry. If they both succeed, there's a good
+ * chance that the device works... */
+#if defined(__linux__)
+static int check_linux_scsi_interface(char *pdev_name)
+{
+ SCSI *dev = NULL;
+ unsigned char *p = NULL;
+ char errstr[80];
+
+ dev = usal_open(pdev_name, errstr, sizeof(errstr), 0, 0);
+ if (NULL == dev)
+ return EINVAL;
+ p = Inquiry(dev);
+ if (p)
+ {
+ usal_close(dev);
+ return 0;
+ }
+ usal_close(dev);
+ return EINVAL;
+}
+#endif
+
+/********************** 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(struct stat *statstruct, char *pdev_name)
+{
+#if defined(__linux__)
+ int is_scsi = 1;
+#endif
+#ifndef STAT_MACROS_BROKEN
+ if (!S_ISCHR(statstruct->st_mode) &&
+ !S_ISBLK(statstruct->st_mode)) {
+ fprintf(stderr, "%s is not a device\n",pdev_name);
+ exit(SYNTAX_ERROR);
+ }
+#endif
+
+/* Check what type of device we have */
+#if defined (__linux__)
+ if (check_linux_scsi_interface(pdev_name))
+ is_scsi = 0;
+ if (interface == GENERIC_SCSI && !is_scsi)
+ {
+ fprintf(stderr, "device %s does not support generic_scsi; falling back to cooked_ioctl instead\n", pdev_name);
+ interface = COOKED_IOCTL;
+ }
+ if ((interface == COOKED_IOCTL) &&
+ is_scsi &&
+ (SCSI_GENERIC_MAJOR == major(statstruct->st_rdev)))
+ {
+ fprintf(stderr, "device %s is generic_scsi NOT cooked_ioctl\n", pdev_name);
+ interface = GENERIC_SCSI;
+ }
+#else
+
+#if defined (HAVE_ST_RDEV)
+ switch (major(statstruct->st_rdev)) {
+#if defined (__linux__)
+ case SCSI_GENERIC_MAJOR: /* generic */
+#else
+ default: /* ??? what is the proper value here */
+#endif
+#ifndef STAT_MACROS_BROKEN
+#if defined (__linux__)
+ if (!S_ISCHR(statstruct->st_mode)) {
+ fprintf(stderr, "%s is not a char device\n",pdev_name);
+ exit(SYNTAX_ERROR);
+ }
+
+ if (interface != GENERIC_SCSI) {
+ fprintf(stderr, "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(__DragonFly__) || defined(__FreeBSD_kernel__)
+#if defined (__linux__)
+ case SCSI_CDROM_MAJOR: /* scsi cd */
+ default: /* for example ATAPI cds */
+#else
+#if defined (__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#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)) {
+ fprintf(stderr, "%s is not a char device\n",pdev_name);
+ exit(SYNTAX_ERROR);
+ }
+ if (interface != COOKED_IOCTL) {
+ fprintf(stderr,
+"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)) {
+ fprintf(stderr, "%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(stderr,
+"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(stderr, "\nW: The cooked_ioctl interface is functionally very limited!!\n");
+#if defined (__linux__)
+ fprintf(stderr, "\nW: For good sampling quality simply use the generic SCSI interface!\n"
+ "For example dev=ATA:1,0,0\n");
+#endif
+ }
+
+ break;
+#endif
+ }
+#endif
+#endif
+ if (global.overlap >= global.nsectors)
+ global.overlap = global.nsectors-1;
+}
+
+/* open the cdrom device */
+static int OpenCdRom(char *pdev_name)
+{
+ int retval = 0;
+ struct stat fstatstruct;
+
+ /* 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
+ struct stat statstruct;
+ int have_named_device = 0;
+
+ have_named_device = FALSE;
+ if (pdev_name) {
+ have_named_device = strchr(pdev_name, ':') == NULL
+ && memcmp(pdev_name, "/dev/", 5) == 0;
+ }
+
+ if (have_named_device) {
+ if (stat(pdev_name, &statstruct)) {
+ fprintf(stderr, "cannot stat device %s\n", pdev_name);
+ exit(STAT_ERROR);
+ } else {
+ Check_interface_for_device( &statstruct, pdev_name );
+ }
+ }
+#endif
+
+ if (interface == GENERIC_SCSI) {
+ char errstr[80];
+
+ priv_on();
+ needroot(0);
+ needgroup(0);
+ /*
+ * 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 (pdev_name != NULL &&
+ ((strncmp(pdev_name, "HELP", 4) == 0) ||
+ (strncmp(pdev_name, "help", 4) == 0))) {
+ usal_help(stderr);
+ exit(NO_ERROR);
+ }
+ /* device name, debug, verboseopen */
+ usalp = usal_open(pdev_name, errstr, sizeof(errstr), 0, 0);
+
+ if (usalp == NULL) {
+ int err = geterrno();
+
+ errmsgno(err, "%s%sCannot open SCSI driver.\n", errstr, errstr[0]?". ":"");
+ errmsgno(EX_BAD, "For possible targets try 'wodim -scanbus'.%s\n",
+ geteuid() ? " Make sure you are root.":"");
+ priv_off();
+ dontneedgroup();
+ dontneedroot();
+#if defined(sun) || defined(__sun)
+ fprintf(stderr, "On SunOS/Solaris make sure you have Joerg Schillings usal 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 'wodim dev=help'.\n");
+ exit(SYNTAX_ERROR);
+ }
+ usal_settimeout(usalp, 300);
+ usal_settimeout(usalp, 60);
+ usalp->silent = global.scsi_silent;
+ usalp->verbose = global.scsi_verbose;
+
+ if (global.nsectors > (unsigned) usal_bufsize(usalp, 3*1024*1024)/CD_FRAMESIZE_RAW)
+ global.nsectors = usal_bufsize(usalp, 3*1024*1024)/CD_FRAMESIZE_RAW;
+ 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(usalp, global.nsectors*CD_FRAMESIZE_RAW);
+ priv_off();
+ dontneedgroup();
+ dontneedroot();
+
+ if (global.scandevs) {
+ list_devices(usalp, stdout, 0);
+ exit(0);
+ }
+
+ if (global.scanbus) {
+ select_target(usalp, stdout);
+ exit(0);
+ }
+ } else {
+ needgroup(0);
+ retval = open(pdev_name,O_RDONLY
+#ifdef linux
+ | O_NONBLOCK
+#endif
+ );
+ dontneedgroup();
+
+ if (retval < 0) {
+ fprintf(stderr, "while opening %s :", pdev_name);
+ perror("");
+ exit(DEVICEOPEN_ERROR);
+ }
+
+ /* Do final security checks here */
+ if (fstat(retval, &fstatstruct)) {
+ fprintf(stderr, "Could not fstat %s (fd %d): ", pdev_name, retval);
+ perror("");
+ 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)) {
+ fprintf(stderr,"Race condition attempted in OpenCdRom. Exiting now.\n");
+ exit(RACE_ERROR);
+ }
+#endif
+ /*
+ * 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 (usalp != NULL) {
+ usalp->verbose = global.scsi_verbose;
+ }
+ }
+ return retval;
+}
+#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(SCSI *x, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+static int ReadCdRom_sim(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(SCSI *x, unsigned int from_sector, unsigned int sectors);
+static int Play_at_sim(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(SCSI *x, TOC *toc);
+static unsigned ReadToc_sim(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(SCSI *usalp, 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(SCSI *usalp, 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_frame = sim_pos2 % 75;
+
+ return (subq_chnl *)(SubQbuffer);
+}
+
+static void SelectSpeed_sim(SCSI *x, unsigned sp);
+/* ARGSUSED */
+static void SelectSpeed_sim(SCSI *x, unsigned sp)
+{
+}
+
+static void trash_cache_sim(UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+
+/* ARGSUSED */
+static void trash_cache_sim(UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal)
+{
+}
+
+static void SetupSimCd(void);
+
+static void SetupSimCd()
+{
+ EnableCdda = (void (*)(SCSI *, int, unsigned))Dummy;
+ ReadCdRom = ReadCdRom_sim;
+ ReadCdRomData = (int (*)(SCSI *, unsigned char *, unsigned, unsigned))ReadCdRom_sim;
+ doReadToc = ReadToc_sim;
+ ReadTocText = NULL;
+ ReadSubQ = ReadSubQ_sim;
+ ReadSubChannels = NULL;
+ ReadLastAudio = NULL;
+ SelectSpeed = SelectSpeed_sim;
+ Play_at = Play_at_sim;
+ StopPlay = (int (*)(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
+
+ global.pagesize = getpagesize();
+
+ /* request one sector for table of contents */
+ bufferTOC = malloc( CD_FRAMESIZE_RAW + 96 ); /* assumes sufficient aligned addresses */
+ /* SubQchannel buffer */
+ SubQbuffer = malloc( 48 ); /* assumes sufficient aligned addresses */
+ cmd = malloc( 18 ); /* assumes sufficient aligned addresses */
+ if ( !bufferTOC || !SubQbuffer || !cmd ) {
+ fprintf( stderr, "Too low on memory. Giving up.\n");
+ exit(NOMEM_ERROR);
+ }
+
+#if defined SIM_CD
+ usalp = malloc(sizeof(* usalp));
+ if (usalp == NULL) {
+ FatalError("No memory for SCSI structure.\n");
+ }
+ usalp->silent = 0;
+ SetupSimCd();
+#else
+ /* if drive is of type scsi, get vendor name */
+ if (interface == GENERIC_SCSI) {
+ unsigned sector_size;
+
+ SetupSCSI();
+ sector_size = get_orig_sectorsize(usalp, &orgmode4, &orgmode10, &orgmode11);
+ if (!SCSI_emulated_ATAPI_on(usalp)) {
+ if ( sector_size != 2048 && set_sectorsize(usalp, 2048) ) {
+ fprintf( stderr, "Could not change sector size from %d to 2048\n", sector_size );
+ }
+ } else {
+ sector_size = 2048;
+ }
+
+ /* get cache setting */
+
+ /* set cache to zero */
+
+ } else {
+#if defined (HAVE_IOCTL_INTERFACE)
+ usalp = malloc(sizeof(* usalp));
+ if (usalp == NULL) {
+ FatalError("No memory for SCSI structure.\n");
+ }
+ usalp->silent = 0;
+ SetupCookedIoctl( global.dev_name );
+#else
+ FatalError("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 (usalp != NULL) {
+ usalp->verbose = global.scsi_verbose;
+ }
+}
+
+#ifdef HAVE_PRIV_H
+#include <priv.h>
+#endif
+
+void
+priv_init()
+{
+#ifdef HAVE_PRIV_SET
+ /*
+ * Give up privs we do not need anymore.
+ * We no longer need:
+ * file_dac_read,sys_devices,proc_priocntl,net_privaddr
+ */
+ priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
+ PRIV_NET_PRIVADDR, NULL);
+ priv_set(PRIV_OFF, PRIV_INHERITABLE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
+ PRIV_NET_PRIVADDR, PRIV_SYS_DEVICES, NULL);
+#endif
+}
+
+void
+priv_on()
+{
+#ifdef HAVE_PRIV_SET
+ /*
+ * Get back privs we may need now.
+ * We need:
+ * file_dac_read,sys_devices,proc_priocntl,net_privaddr
+ */
+ priv_set(PRIV_ON, PRIV_EFFECTIVE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
+ PRIV_NET_PRIVADDR, NULL);
+#endif
+}
+
+void
+priv_off()
+{
+#ifdef HAVE_PRIV_SET
+ /*
+ * Give up privs we do not need anymore.
+ * We no longer need:
+ * file_dac_read,sys_devices,proc_priocntl,net_privaddr
+ */
+ priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+ PRIV_FILE_DAC_READ, PRIV_PROC_PRIOCNTL,
+ PRIV_NET_PRIVADDR, NULL);
+#endif
+}