summaryrefslogtreecommitdiff
path: root/icedax/scsi_cmds.c
diff options
context:
space:
mode:
Diffstat (limited to 'icedax/scsi_cmds.c')
-rw-r--r--icedax/scsi_cmds.c1720
1 files changed, 1720 insertions, 0 deletions
diff --git a/icedax/scsi_cmds.c b/icedax/scsi_cmds.c
new file mode 100644
index 0000000..e77b3af
--- /dev/null
+++ b/icedax/scsi_cmds.c
@@ -0,0 +1,1720 @@
+/*
+ * 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_cmds.c 1.29 03/03/31 Copyright 1998-2002 Heiko Eissfeldt */
+/* file for all SCSI commands
+ * FUA (Force Unit Access) bit handling copied from Monty's cdparanoia.
+ */
+#undef DEBUG_FULLTOC
+#undef WARN_FULLTOC
+#define TESTSUBQFALLBACK 0
+
+#include "config.h"
+#include <stdio.h>
+#include <standard.h>
+#include <stdxlib.h>
+#include <strdefs.h>
+#include <schily.h>
+
+#include <btorder.h>
+
+#define g5x_cdblen(cdb, len) ((cdb)->count[0] = ((len) >> 16L)& 0xFF,\
+ (cdb)->count[1] = ((len) >> 8L) & 0xFF,\
+ (cdb)->count[2] = (len) & 0xFF)
+
+
+#include <usal/usalcmd.h>
+#include <usal/scsidefs.h>
+#include <usal/scsireg.h>
+
+#include <usal/scsitransp.h>
+
+#include "mytype.h"
+#include "icedax.h"
+#include "interface.h"
+#include "byteorder.h"
+#include "global.h"
+#include "wodim.h"
+#include "toc.h"
+#include "scsi_cmds.h"
+#include "exitcodes.h"
+
+unsigned char *bufferTOC;
+subq_chnl *SubQbuffer;
+unsigned char *cmd;
+
+static unsigned ReadFullTOCSony(SCSI *usalp);
+static unsigned ReadFullTOCMMC(SCSI *usalp);
+
+
+int SCSI_emulated_ATAPI_on(SCSI *usalp)
+{
+/* return is_atapi;*/
+ if (usal_isatapi(usalp) > 0)
+ return (TRUE);
+
+ (void) allow_atapi(usalp, TRUE);
+ return (allow_atapi(usalp, TRUE));
+}
+
+int heiko_mmc(SCSI *usalp)
+{
+ unsigned char mode[0x100];
+ int was_atapi;
+ struct cd_mode_page_2A *mp;
+ int retval;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+
+ was_atapi = allow_atapi(usalp, 1);
+ usalp->silent++;
+ mp = mmc_cap(usalp, mode);
+ usalp->silent--;
+ allow_atapi(usalp, was_atapi);
+ if (mp == NULL)
+ return (0);
+
+ /* have a look at the capabilities */
+ if (mp->cd_da_supported == 0) {
+ retval = -1;
+ } else {
+ retval = 1 + mp->cd_da_accurate;
+ }
+ return retval;
+}
+
+
+int accepts_fua_bit;
+unsigned char density = 0;
+unsigned char orgmode4 = 0;
+unsigned char orgmode10, orgmode11;
+
+/* get current sector size from SCSI cdrom drive */
+unsigned int
+get_orig_sectorsize(SCSI *usalp, unsigned char *m4, unsigned char *m10,
+ unsigned char *m11)
+{
+ /* first get current values for density, etc. */
+
+ static unsigned char *modesense = NULL;
+
+ if (modesense == NULL) {
+ modesense = malloc(12);
+ if (modesense == NULL) {
+ fprintf(stderr, "Cannot allocate memory for mode sense command in line %d\n", __LINE__);
+ return 0;
+ }
+ }
+
+ /* do the scsi cmd */
+ if (usalp->verbose) fprintf(stderr, "\nget density and sector size...");
+ if (mode_sense(usalp, modesense, 12, 0x01, 0) < 0)
+ fprintf(stderr, "get_orig_sectorsize mode sense failed\n");
+
+ /* FIXME: some drives dont deliver block descriptors !!! */
+ if (modesense[3] == 0)
+ return 0;
+
+#if 0
+ modesense[4] = 0x81;
+ modesense[10] = 0x08;
+ modesense[11] = 0x00;
+#endif
+
+ if (m4 != NULL) /* density */
+ *m4 = modesense[4];
+ if (m10 != NULL) /* MSB sector size */
+ *m10 = modesense[10];
+ if (m11 != NULL) /* LSB sector size */
+ *m11 = modesense[11];
+
+ return (modesense[10] << 8) + modesense[11];
+}
+
+
+
+/* switch CDROM scsi drives to given sector size */
+int set_sectorsize(SCSI *usalp, unsigned int secsize)
+{
+ static unsigned char mode [4 + 8];
+ int retval;
+
+ if (orgmode4 == 0xff) {
+ get_orig_sectorsize(usalp, &orgmode4, &orgmode10, &orgmode11);
+ }
+ if (orgmode4 == 0x82 && secsize == 2048)
+ orgmode4 = 0x81;
+
+ /* prepare to read cds in the previous mode */
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ mode[ 3] = 8; /* Block Descriptor Length */
+ mode[ 4] = orgmode4; /* normal density */
+ mode[10] = secsize >> 8; /* block length "msb" */
+ mode[11] = secsize & 0xFF; /* block length lsb */
+
+ if (usalp->verbose) fprintf(stderr, "\nset density and sector size...");
+ /* do the scsi cmd */
+ if ((retval = mode_select(usalp, mode, 12, 0, usalp->inq->data_format >= 2)) < 0)
+ fprintf (stderr, "setting sector size failed\n");
+
+ return retval;
+}
+
+
+/* switch Toshiba/DEC and HP drives from/to cdda density */
+void EnableCddaModeSelect(SCSI *usalp, int fAudioMode, unsigned uSectorsize)
+{
+ /* reserved, Medium type=0, Dev spec Parm = 0, block descriptor len 0 oder 8,
+ Density (cd format)
+ (0=YellowBook, XA Mode 2=81h, XA Mode1=83h and raw audio via SCSI=82h),
+ # blks msb, #blks, #blks lsb, reserved,
+ blocksize, blocklen msb, blocklen lsb,
+ */
+
+ /* MODE_SELECT, page = SCSI-2 save page disabled, reserved, reserved,
+ parm list len, flags */
+ static unsigned char mode [4 + 8] = {
+ /* mode section */
+ 0,
+ 0, 0,
+ 8, /* Block Descriptor Length */
+ /* block descriptor */
+ 0, /* Density Code */
+ 0, 0, 0, /* # of Blocks */
+ 0, /* reserved */
+ 0, 0, 0};/* Blocklen */
+
+ if (orgmode4 == 0 && fAudioMode) {
+ if (0 == get_orig_sectorsize(usalp, &orgmode4, &orgmode10, &orgmode11)) {
+ /* cannot retrieve density, sectorsize */
+ orgmode10 = (CD_FRAMESIZE >> 8L);
+ orgmode11 = (CD_FRAMESIZE & 0xFF);
+ }
+ }
+
+ if (fAudioMode) {
+ /* prepare to read audio cdda */
+ mode [4] = density; /* cdda density */
+ mode [10] = (uSectorsize >> 8L); /* block length "msb" */
+ mode [11] = (uSectorsize & 0xFF); /* block length "lsb" */
+ } else {
+ /* prepare to read cds in the previous mode */
+ mode [4] = orgmode4; /* 0x00; \* normal density */
+ mode [10] = orgmode10; /* (CD_FRAMESIZE >> 8L); \* block length "msb" */
+ mode [11] = orgmode11; /* (CD_FRAMESIZE & 0xFF); \* block length lsb */
+ }
+
+ if (usalp->verbose) fprintf(stderr, "\nset density/sector size (EnableCddaModeSelect)...\n");
+ /* do the scsi cmd */
+ if (mode_select(usalp, mode, 12, 0, usalp->inq->data_format >= 2) < 0)
+ fprintf (stderr, "Audio mode switch failed\n");
+}
+
+
+/* read CD Text information from the table of contents */
+void ReadTocTextSCSIMMC(SCSI *usalp)
+{
+ short datalength;
+
+#if 1
+ /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
+ len lsb, control */
+ unsigned char *p = bufferTOC;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 4;
+ 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; /* Read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 5; /* format field */
+ scmd->cdb.g1_cdb.res6 = 0; /* track/session is reserved */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4);
+
+ usalp->silent++;
+ if (usalp->verbose) fprintf(stderr, "\nRead TOC CD Text size ...");
+
+ usalp->cmdname = "read toc size (text)";
+
+ if (usal_cmd(usalp) < 0) {
+ usalp->silent--;
+ if (global.quiet != 1)
+ fprintf (stderr, "Read TOC CD Text failed (probably not supported).\n");
+ p[0] = p[1] = '\0';
+ return ;
+ }
+ usalp->silent--;
+
+ datalength = (p[0] << 8) | (p[1]);
+ if (datalength <= 2)
+ return;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 2+datalength;
+ 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; /* Read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 5; /* format field */
+ scmd->cdb.g1_cdb.res6 = 0; /* track/session is reserved */
+ g1_cdblen(&scmd->cdb.g1_cdb, 2+datalength);
+
+ usalp->silent++;
+ if (usalp->verbose) fprintf(stderr, "\nRead TOC CD Text data (length %hd)...", 2+datalength);
+
+ usalp->cmdname = "read toc data (text)";
+
+ if (usal_cmd(usalp) < 0) {
+ usalp->silent--;
+ if (global.quiet != 1)
+ fprintf (stderr, "Read TOC CD Text data failed (probably not supported).\n");
+ p[0] = p[1] = '\0';
+ return ;
+ }
+ usalp->silent--;
+#else
+ { FILE *fp;
+ int read_;
+ /*fp = fopen("PearlJam.cdtext", "rb");*/
+ /*fp = fopen("celine.cdtext", "rb");*/
+ fp = fopen("japan.cdtext", "rb");
+ if (fp == NULL) { perror(""); return; }
+ fillbytes(bufferTOC, CD_FRAMESIZE, '\0');
+ read_ = fread(bufferTOC, 1, CD_FRAMESIZE, fp );
+fprintf(stderr, "read %d bytes. sizeof(bufferTOC)=%u\n", read_, CD_FRAMESIZE);
+ datalength = (bufferTOC[0] << 8) | (bufferTOC[1]);
+ fclose(fp);
+ }
+#endif
+}
+
+/* read the full TOC */
+static unsigned ReadFullTOCSony(SCSI *usalp)
+{
+ /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
+ len lsb, control */
+ register struct usal_cmd *scmd = usalp->scmd;
+ unsigned tracks = 99;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 4 + (3 + tracks + 6) * 11;
+ 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; /* Read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res6 = 1; /* session */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4 + (3 + tracks + 6) * 11);
+ scmd->cdb.g1_cdb.vu_97 = 1; /* format */
+
+ usalp->silent++;
+ if (usalp->verbose) fprintf(stderr, "\nRead Full TOC Sony ...");
+
+ usalp->cmdname = "read full toc sony";
+
+ if (usal_cmd(usalp) < 0) {
+ usalp->silent--;
+ if (global.quiet != 1)
+ fprintf (stderr, "Read Full TOC Sony failed (probably not supported).\n");
+ return 0;
+ }
+ usalp->silent--;
+
+ return (unsigned)((bufferTOC[0] << 8) | bufferTOC[1]);
+}
+
+struct msf_address {
+ unsigned char mins;
+ unsigned char secs;
+ unsigned char frame;
+};
+
+struct zmsf_address {
+ unsigned char zero;
+ unsigned char mins;
+ unsigned char secs;
+ unsigned char frame;
+};
+
+#ifdef WARN_FULLTOC
+static unsigned lba(struct msf_address *ad);
+
+static unsigned lba(struct msf_address *ad)
+{
+ return ad->mins*60*75 + ad->secs*75 + ad->frame;
+}
+#endif
+
+static unsigned dvd_lba(struct zmsf_address *ad);
+
+static unsigned dvd_lba(struct zmsf_address *ad)
+{
+ return ad->zero*1053696 + ad->mins*60*75 + ad->secs*75 + ad->frame;
+}
+
+struct tocdesc {
+ unsigned char session;
+ unsigned char adrctl;
+ unsigned char tno;
+ unsigned char point;
+ struct msf_address adr1;
+ struct zmsf_address padr2;
+};
+
+struct outer {
+ unsigned char len_msb;
+ unsigned char len_lsb;
+ unsigned char first_track;
+ unsigned char last_track;
+ struct tocdesc ent[1];
+};
+
+static unsigned long first_session_leadout = 0;
+
+static unsigned collect_tracks(struct outer *po, unsigned entries,
+ BOOL bcd_flag);
+
+static unsigned collect_tracks(struct outer *po, unsigned entries,
+ BOOL bcd_flag)
+{
+ unsigned tracks = 0;
+ int i;
+ unsigned session;
+ unsigned last_start;
+ unsigned leadout_start_orig;
+ unsigned leadout_start;
+ unsigned max_leadout = 0;
+
+#ifdef DEBUG_FULLTOC
+ for (i = 0; i < entries; i++) {
+fprintf(stderr, "%3d: %d %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ ,i
+ ,bufferTOC[4+0 + (i * 11)]
+ ,bufferTOC[4+1 + (i * 11)]
+ ,bufferTOC[4+2 + (i * 11)]
+ ,bufferTOC[4+3 + (i * 11)]
+ ,bufferTOC[4+4 + (i * 11)]
+ ,bufferTOC[4+5 + (i * 11)]
+ ,bufferTOC[4+6 + (i * 11)]
+ ,bufferTOC[4+7 + (i * 11)]
+ ,bufferTOC[4+8 + (i * 11)]
+ ,bufferTOC[4+9 + (i * 11)]
+ ,bufferTOC[4+10 + (i * 11)]
+ );
+ }
+#endif
+ /* reformat to standard toc format */
+
+ bufferTOC[2] = 0;
+ bufferTOC[3] = 0;
+ session = 0;
+ last_start = 0;
+ leadout_start_orig = 0;
+ leadout_start = 0;
+
+ for (i = 0; i < entries; i++) {
+#ifdef WARN_FULLTOC
+ if (po->ent[i].tno != 0) {
+ fprintf(stderr,
+"entry %d, tno is not 0: %d!\n",
+i, po->ent[i].tno);
+ }
+#endif
+ if (bcd_flag) {
+ po->ent[i].session = from_bcd(po->ent[i].session);
+ po->ent[i].adr1.mins = from_bcd(po->ent[i].adr1.mins);
+ po->ent[i].adr1.secs = from_bcd(po->ent[i].adr1.secs);
+ po->ent[i].adr1.frame = from_bcd(po->ent[i].adr1.frame);
+ po->ent[i].padr2.mins = from_bcd(po->ent[i].padr2.mins);
+ po->ent[i].padr2.secs = from_bcd(po->ent[i].padr2.secs);
+ po->ent[i].padr2.frame = from_bcd(po->ent[i].padr2.frame);
+ }
+ switch (po->ent[i].point) {
+ case 0xa0:
+
+ /* check if session is monotonous increasing */
+
+ if (session+1 == po->ent[i].session) {
+ session = po->ent[i].session;
+ }
+#ifdef WARN_FULLTOC
+ else fprintf(stderr,
+"entry %d, session anomaly %d != %d!\n",
+i, session+1, po->ent[i].session);
+
+ /* check the adrctl field */
+ if (0x10 != (po->ent[i].adrctl & 0x10)) {
+ fprintf(stderr,
+"entry %d, incorrect adrctl field %x!\n",
+i, po->ent[i].adrctl);
+ }
+#endif
+ /* first track number */
+ if (bufferTOC[2] < po->ent[i].padr2.mins
+ && bufferTOC[3] < po->ent[i].padr2.mins) {
+ bufferTOC[2] = po->ent[i].padr2.mins;
+ }
+#ifdef WARN_FULLTOC
+ else
+ fprintf(stderr,
+"entry %d, session %d: start tracknumber anomaly: %d <= %d,%d(last)!\n",
+i, session, po->ent[i].padr2.mins, bufferTOC[2], bufferTOC[3]);
+#endif
+ break;
+
+ case 0xa1:
+#ifdef WARN_FULLTOC
+ /* check if session is constant */
+ if (session != po->ent[i].session) {
+ fprintf(stderr,
+"entry %d, session anomaly %d != %d!\n",
+i, session, po->ent[i].session);
+ }
+
+ /* check the adrctl field */
+ if (0x10 != (po->ent[i].adrctl & 0x10)) {
+ fprintf(stderr,
+"entry %d, incorrect adrctl field %x!\n",
+i, po->ent[i].adrctl);
+ }
+#endif
+ /* last track number */
+ if (bufferTOC[2] <= po->ent[i].padr2.mins
+ && bufferTOC[3] < po->ent[i].padr2.mins) {
+ bufferTOC[3] = po->ent[i].padr2.mins;
+ }
+#ifdef WARN_FULLTOC
+ else
+ fprintf(stderr,
+"entry %d, session %d: end tracknumber anomaly: %d <= %d,%d(last)!\n",
+i, session, po->ent[i].padr2.mins, bufferTOC[2], bufferTOC[3]);
+#endif
+ break;
+
+ case 0xa2:
+#ifdef WARN_FULLTOC
+ /* check if session is constant */
+ if (session != po->ent[i].session) {
+ fprintf(stderr,
+"entry %d, session anomaly %d != %d!\n",
+i, session, po->ent[i].session);
+ }
+
+ /* check the adrctl field */
+ if (0x10 != (po->ent[i].adrctl & 0x10)) {
+ fprintf(stderr,
+"entry %d, incorrect adrctl field %x!\n",
+i, po->ent[i].adrctl);
+ }
+#endif
+ /* register leadout position */
+ {
+ unsigned leadout_start_tmp =
+ dvd_lba(&po->ent[i].padr2);
+
+ if (first_session_leadout == 0)
+ first_session_leadout = leadout_start_tmp - 150;
+
+ if (leadout_start_tmp > leadout_start) {
+ leadout_start_orig = leadout_start_tmp;
+ leadout_start = leadout_start_tmp;
+ }
+#ifdef WARN_FULLTOC
+ else
+ fprintf(stderr,
+"entry %d, leadout position anomaly %u!\n",
+i, leadout_start_tmp);
+#endif
+ }
+ break;
+
+ case 0xb0:
+#ifdef WARN_FULLTOC
+ /* check if session is constant */
+ if (session != po->ent[i].session) {
+ fprintf(stderr,
+"entry %d, session anomaly %d != %d!\n",
+i, session, po->ent[i].session);
+ }
+
+ /* check the adrctl field */
+ if (0x50 != (po->ent[i].adrctl & 0x50)) {
+ fprintf(stderr,
+"entry %d, incorrect adrctl field %x!\n",
+i, po->ent[i].adrctl);
+ }
+
+ /* check the next program area */
+ if (lba(&po->ent[i].adr1) < 6750 + leadout_start) {
+ fprintf(stderr,
+"entry %d, next program area %u < leadout_start + 6750 = %u!\n",
+i, lba(&po->ent[i].adr1), 6750 + leadout_start);
+ }
+
+ /* check the maximum leadout_start */
+ if (max_leadout != 0 && dvd_lba(&po->ent[i].padr2) != max_leadout) {
+ fprintf(stderr,
+"entry %d, max leadout_start %u != last max_leadout_start %u!\n",
+i, dvd_lba(&po->ent[i].padr2), max_leadout);
+ }
+#endif
+ if (max_leadout == 0)
+ max_leadout = dvd_lba(&po->ent[i].padr2);
+
+ break;
+ case 0xb1:
+ case 0xb2:
+ case 0xb3:
+ case 0xb4:
+ case 0xb5:
+ case 0xb6:
+ break;
+ case 0xc0:
+ case 0xc1:
+ break;
+ default:
+ /* check if session is constant */
+ if (session != po->ent[i].session) {
+#ifdef WARN_FULLTOC
+ fprintf(stderr,
+"entry %d, session anomaly %d != %d!\n",
+i, session, po->ent[i].session);
+#endif
+ continue;
+ }
+
+ /* check tno */
+ if (bcd_flag)
+ po->ent[i].point = from_bcd(po->ent[i].point);
+
+ if (po->ent[i].point < bufferTOC[2]
+ || po->ent[i].point > bufferTOC[3]) {
+#ifdef WARN_FULLTOC
+ fprintf(stderr,
+"entry %d, track number anomaly %d - %d - %d!\n",
+i, bufferTOC[2], po->ent[i].point, bufferTOC[3]);
+#endif
+ } else {
+ /* check start position */
+ unsigned trackstart = dvd_lba(&po->ent[i].padr2);
+
+ /* correct illegal leadouts */
+ if (leadout_start < trackstart) {
+ leadout_start = trackstart+1;
+ }
+ if (trackstart < last_start || trackstart >= leadout_start) {
+#ifdef WARN_FULLTOC
+ fprintf(stderr,
+"entry %d, track %d start position anomaly %d - %d - %d!\n",
+i, po->ent[i].point, last_start, trackstart, leadout_start);
+#endif
+ } else {
+ last_start = trackstart;
+ memcpy(&po->ent[tracks], &po->ent[i], sizeof(struct tocdesc));
+ tracks++;
+ }
+ }
+ } /* switch */
+ } /* for */
+
+ /* patch leadout track */
+ po->ent[tracks].session = session;
+ po->ent[tracks].adrctl = 0x10;
+ po->ent[tracks].tno = 0;
+ po->ent[tracks].point = 0xAA;
+ po->ent[tracks].adr1.mins = 0;
+ po->ent[tracks].adr1.secs = 0;
+ po->ent[tracks].adr1.frame = 0;
+ po->ent[tracks].padr2.zero = leadout_start_orig / (1053696);
+ po->ent[tracks].padr2.mins = (leadout_start_orig / (60*75)) % 100;
+ po->ent[tracks].padr2.secs = (leadout_start_orig / 75) % 60;
+ po->ent[tracks].padr2.frame = leadout_start_orig % 75;
+ tracks++;
+
+ /* length */
+ bufferTOC[0] = ((tracks * 8) + 2) >> 8;
+ bufferTOC[1] = ((tracks * 8) + 2) & 0xff;
+
+
+ /* reformat 11 byte blocks to 8 byte entries */
+
+ /* 1: Session \ / reserved
+ 2: adr ctrl | | adr ctrl
+ 3: TNO | | track number
+ 4: Point | | reserved
+ 5: Min +-->----+ 0
+ 6: Sec | | Min
+ 7: Frame | | Sec
+ 8: Zero | \ Frame
+ 9: PMin |
+ 10: PSec |
+ 11: PFrame /
+ */
+ for (i = 0; i < tracks; i++) {
+ bufferTOC[4+0 + (i << 3)] = 0;
+ bufferTOC[4+1 + (i << 3)] = bufferTOC[4+1 + (i*11)];
+ bufferTOC[4+1 + (i << 3)] = (bufferTOC[4+1 + (i << 3)] >> 4) | (bufferTOC[4+1 + (i << 3)] << 4);
+ bufferTOC[4+2 + (i << 3)] = bufferTOC[4+3 + (i*11)];
+ bufferTOC[4+3 + (i << 3)] = 0;
+ bufferTOC[4+4 + (i << 3)] = bufferTOC[4+7 + (i*11)];
+ bufferTOC[4+5 + (i << 3)] = bufferTOC[4+8 + (i*11)];
+ bufferTOC[4+6 + (i << 3)] = bufferTOC[4+9 + (i*11)];
+ bufferTOC[4+7 + (i << 3)] = bufferTOC[4+10 + (i*11)];
+#ifdef DEBUG_FULLTOC
+fprintf(stderr, "%02x %02x %02x %02x %02x %02x\n"
+ ,bufferTOC[4+ 1 + i*8]
+ ,bufferTOC[4+ 2 + i*8]
+ ,bufferTOC[4+ 4 + i*8]
+ ,bufferTOC[4+ 5 + i*8]
+ ,bufferTOC[4+ 6 + i*8]
+ ,bufferTOC[4+ 7 + i*8]
+);
+#endif
+ }
+
+ TOC_entries(tracks, NULL, bufferTOC+4, 0);
+ return tracks;
+}
+
+/* read the table of contents from the cd and fill the TOC array */
+unsigned ReadTocSony(SCSI *usalp)
+{
+ unsigned tracks = 0;
+ unsigned return_length;
+
+ struct outer *po = (struct outer *)bufferTOC;
+
+ return_length = ReadFullTOCSony(usalp);
+
+ /* Check if the format was understood */
+ if ((return_length & 7) == 2 && (bufferTOC[3] - bufferTOC[2]) == (return_length >> 3)) {
+ /* The extended format seems not be understood, fallback to
+ * the classical format. */
+ return ReadTocSCSI( usalp );
+ }
+
+ tracks = collect_tracks(po, ((return_length - 2) / 11), TRUE);
+
+ return --tracks; /* without lead-out */
+}
+
+/* read the start of the lead-out from the first session TOC */
+unsigned ReadFirstSessionTOCSony(SCSI *usalp)
+{
+ unsigned return_length;
+
+ if (first_session_leadout != 0)
+ return first_session_leadout;
+
+ return_length = ReadFullTOCSony(usalp);
+ if (return_length >= 4 + (3 * 11) -2) {
+ unsigned off;
+
+ /* We want the entry with POINT = 0xA2, which has the start position
+ of the first session lead out */
+ off = 4 + 2 * 11 + 3;
+ if (bufferTOC[off-3] == 1 && bufferTOC[off] == 0xA2) {
+ unsigned retval;
+
+ off = 4 + 2 * 11 + 8;
+ retval = bufferTOC[off] >> 4;
+ retval *= 10; retval += bufferTOC[off] & 0xf;
+ retval *= 60;
+ off++;
+ retval += 10 * (bufferTOC[off] >> 4) + (bufferTOC[off] & 0xf);
+ retval *= 75;
+ off++;
+ retval += 10 * (bufferTOC[off] >> 4) + (bufferTOC[off] & 0xf);
+ retval -= 150;
+
+ return retval;
+ }
+ }
+ return 0;
+}
+
+/* read the full TOC */
+static unsigned ReadFullTOCMMC(SCSI *usalp)
+{
+
+ /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
+ len lsb, control */
+ register struct usal_cmd *scmd = usalp->scmd;
+ unsigned tracks = 99;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 4 + (tracks + 8) * 11;
+ 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; /* Read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 2; /* format */
+ scmd->cdb.g1_cdb.res6 = 1; /* session */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4 + (tracks + 8) * 11);
+
+ usalp->silent++;
+ if (usalp->verbose) fprintf(stderr, "\nRead Full TOC MMC...");
+
+ usalp->cmdname = "read full toc mmc";
+
+ if (usal_cmd(usalp) < 0) {
+ if (global.quiet != 1)
+ fprintf (stderr, "Read Full TOC MMC failed (probably not supported).\n");
+#ifdef B_BEOS_VERSION
+#else
+ usalp->silent--;
+ return 0;
+#endif
+ }
+ usalp->silent--;
+
+ return (unsigned)((bufferTOC[0] << 8) | bufferTOC[1]);
+}
+
+/* read the start of the lead-out from the first session TOC */
+unsigned ReadFirstSessionTOCMMC(SCSI *usalp)
+{
+ unsigned off;
+ unsigned return_length;
+
+ if (first_session_leadout != 0)
+ return first_session_leadout;
+
+ return_length = ReadFullTOCMMC(usalp);
+
+ /* We want the entry with POINT = 0xA2, which has the start position
+ of the first session lead out */
+ off = 4 + 3;
+ while (off < return_length && bufferTOC[off] != 0xA2) {
+ off += 11;
+ }
+ if (off < return_length) {
+ off += 5;
+ return (bufferTOC[off]*60 + bufferTOC[off+1])*75 + bufferTOC[off+2] - 150;
+ }
+ return 0;
+}
+
+/* read the table of contents from the cd and fill the TOC array */
+unsigned ReadTocMMC(SCSI *usalp)
+{
+ unsigned tracks = 0;
+ unsigned return_length;
+
+ struct outer *po = (struct outer *)bufferTOC;
+
+ return_length = ReadFullTOCMMC(usalp);
+ if (return_length - 2 < 4*11 || ((return_length - 2) % 11) != 0)
+ return ReadTocSCSI(usalp);
+
+ tracks = collect_tracks(po, ((return_length - 2) / 11), FALSE);
+ return --tracks; /* without lead-out */
+}
+
+/* read the table of contents from the cd and fill the TOC array */
+unsigned ReadTocSCSI(SCSI *usalp)
+{
+ unsigned tracks;
+ int result;
+ unsigned char bufferTOCMSF[CD_FRAMESIZE];
+
+ /* first read the first and last track number */
+ /* READTOC, MSF format flag, res, res, res, res, Start track, len msb,
+ len lsb, flags */
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 4;
+ 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; /* read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res6 = 1; /* start track */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4);
+
+ if (usalp->verbose) fprintf(stderr, "\nRead TOC size (standard)...");
+ /* do the scsi cmd (read table of contents) */
+
+ usalp->cmdname = "read toc size";
+ if (usal_cmd(usalp) < 0)
+ FatalError ("Read TOC size failed.\n");
+
+
+ tracks = ((bufferTOC [3] ) - bufferTOC [2] + 2) ;
+ if (tracks > MAXTRK) return 0;
+ if (tracks == 0) return 0;
+
+
+ memset(bufferTOCMSF, 0, sizeof(bufferTOCMSF));
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOCMSF;
+ scmd->size = 4 + tracks * 8;
+ 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; /* read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res = 1; /* MSF format */
+ scmd->cdb.g1_cdb.res6 = 1; /* start track */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4 + tracks * 8);
+
+ if (usalp->verbose) fprintf(stderr, "\nRead TOC tracks (standard MSF)...");
+ /* do the scsi cmd (read table of contents) */
+
+ usalp->cmdname = "read toc tracks ";
+ result = usal_cmd(usalp);
+
+ if (result < 0) {
+ /* MSF format did not succeeded */
+ memset(bufferTOCMSF, 0, sizeof(bufferTOCMSF));
+ } else {
+ int i;
+ for (i = 0; i < tracks; i++) {
+ bufferTOCMSF[4+1 + (i << 3)] = (bufferTOCMSF[4+1 + (i << 3)] >> 4) | (bufferTOCMSF[4+1 + (i << 3)] << 4);
+#if 0
+fprintf(stderr, "MSF %d %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ ,i
+ ,bufferTOCMSF[4+0 + (i * 8)]
+ ,bufferTOCMSF[4+1 + (i * 8)]
+ ,bufferTOCMSF[4+2 + (i * 8)]
+ ,bufferTOCMSF[4+3 + (i * 8)]
+ ,bufferTOCMSF[4+4 + (i * 8)]
+ ,bufferTOCMSF[4+5 + (i * 8)]
+ ,bufferTOCMSF[4+6 + (i * 8)]
+ ,bufferTOCMSF[4+7 + (i * 8)]
+ );
+#endif
+ }
+ }
+
+ /* LBA format for cd burners like Philips CD-522 */
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)bufferTOC;
+ scmd->size = 4 + tracks * 8;
+ 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; /* read TOC command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res = 0; /* LBA format */
+ scmd->cdb.g1_cdb.res6 = 1; /* start track */
+ g1_cdblen(&scmd->cdb.g1_cdb, 4 + tracks * 8);
+
+ if (usalp->verbose) fprintf(stderr, "\nRead TOC tracks (standard LBA)...");
+ /* do the scsi cmd (read table of contents) */
+
+ usalp->cmdname = "read toc tracks ";
+ if (usal_cmd(usalp) < 0) {
+ FatalError ("Read TOC tracks (lba) failed.\n");
+ }
+ {
+ int i;
+ for (i = 0; i < tracks; i++) {
+ bufferTOC[4+1 + (i << 3)] = (bufferTOC[4+1 + (i << 3)] >> 4) | (bufferTOC[4+1 + (i << 3)] << 4);
+#if 0
+fprintf(stderr, "LBA %d %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ ,i
+ ,bufferTOC[4+0 + (i * 8)]
+ ,bufferTOC[4+1 + (i * 8)]
+ ,bufferTOC[4+2 + (i * 8)]
+ ,bufferTOC[4+3 + (i * 8)]
+ ,bufferTOC[4+4 + (i * 8)]
+ ,bufferTOC[4+5 + (i * 8)]
+ ,bufferTOC[4+6 + (i * 8)]
+ ,bufferTOC[4+7 + (i * 8)]
+ );
+#endif
+ }
+ }
+ TOC_entries(tracks, bufferTOC+4, bufferTOCMSF+4, result);
+ return --tracks; /* without lead-out */
+}
+
+/* ---------------- Read methods ------------------------------ */
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via standard SCSI-2 Read(10) command */
+static int ReadStandardLowlevel(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal, unsigned secsize);
+
+static int ReadStandardLowlevel(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal, unsigned secsize)
+{
+ /* READ10, flags, block1 msb, block2, block3, block4 lsb, reserved,
+ transfer len msb, transfer len lsb, block addressing mode */
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal * secsize;
+ 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 = 0x28; /* read 10 command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, lSector);
+ g1_cdblen(&scmd->cdb.g1_cdb, SectorBurstVal);
+ if (usalp->verbose) fprintf(stderr, "\nReadStandard10 %s (%u)...", secsize > 2048 ? "CDDA" : "CD_DATA", secsize);
+
+ usalp->cmdname = "ReadStandard10";
+
+ if (usal_cmd(usalp)) return 0;
+
+ /* has all or something been read? */
+ return SectorBurstVal - usal_getresid(usalp)/secsize;
+}
+
+
+int
+ReadStandard(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ return ReadStandardLowlevel(usalp, p, lSector, SectorBurstVal, CD_FRAMESIZE_RAW);
+}
+
+int
+ReadStandardData(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ return ReadStandardLowlevel(usalp, p, lSector, SectorBurstVal, CD_FRAMESIZE);
+}
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via vendor-specific ReadCdda(10) command */
+int ReadCdda10(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ /* READ10, flags, block1 msb, block2, block3, block4 lsb, reserved,
+ transfer len msb, transfer len lsb, block addressing mode */
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*CD_FRAMESIZE_RAW;
+ 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 = 0xd4; /* Read audio command */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ g1_cdbaddr(&scmd->cdb.g1_cdb, lSector);
+ g1_cdblen(&scmd->cdb.g1_cdb, SectorBurstVal);
+ if (usalp->verbose) fprintf(stderr, "\nReadNEC10 CDDA...");
+
+ usalp->cmdname = "Read10 NEC";
+
+ if (usal_cmd(usalp)) return 0;
+
+ /* has all or something been read? */
+ return SectorBurstVal - usal_getresid(usalp)/CD_FRAMESIZE_RAW;
+}
+
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via vendor-specific ReadCdda(12) command */
+int ReadCdda12(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*CD_FRAMESIZE_RAW;
+ 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 = 0xd8; /* read audio command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+
+ if (usalp->verbose) fprintf(stderr, "\nReadSony12 CDDA...");
+
+ usalp->cmdname = "Read12";
+
+ if (usal_cmd(usalp)) return 0;
+
+ /* has all or something been read? */
+ return SectorBurstVal - usal_getresid(usalp)/CD_FRAMESIZE_RAW;
+}
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via vendor-specific ReadCdda(12) command */
+/*
+> It uses a 12 Byte CDB with 0xd4 as opcode, the start sector is coded as
+> normal and the number of sectors is coded in Byte 8 and 9 (begining with 0).
+*/
+
+int ReadCdda12Matsushita(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*CD_FRAMESIZE_RAW;
+ 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 = 0xd4; /* read audio command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+
+ if (usalp->verbose) fprintf(stderr, "\nReadMatsushita12 CDDA...");
+
+ usalp->cmdname = "Read12Matsushita";
+
+ if (usal_cmd(usalp)) return 0;
+
+ /* has all or something been read? */
+ return SectorBurstVal - usal_getresid(usalp)/CD_FRAMESIZE_RAW;
+}
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via MMC standard READ CD command */
+int ReadCddaMMC12(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd;
+ scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*CD_FRAMESIZE_RAW;
+ 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 = 0xbe; /* read cd command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res = 1 << 1; /* expected sector type field CDDA */
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5x_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+ scmd->cdb.g5_cdb.count[3] = 1 << 4; /* User data */
+
+ if (usalp->verbose) fprintf(stderr, "\nReadMMC12 CDDA...");
+
+ usalp->cmdname = "ReadCD MMC 12";
+
+ if (usal_cmd(usalp)) return 0;
+
+ /* has all or something been read? */
+ return SectorBurstVal - usal_getresid(usalp)/CD_FRAMESIZE_RAW;
+}
+
+int ReadCddaFallbackMMC(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal)
+{
+ static int ReadCdda12_unknown = 0;
+ int retval = -999;
+
+ usalp->silent++;
+ if (ReadCdda12_unknown
+ || ((retval = ReadCdda12(usalp, p, lSector, SectorBurstVal)) <= 0)) {
+ /* if the command is not available, use the regular
+ * MMC ReadCd
+ */
+ if (retval <= 0 && usal_sense_key(usalp) == 0x05) {
+ ReadCdda12_unknown = 1;
+ }
+ usalp->silent--;
+ ReadCdRom = ReadCddaMMC12;
+ ReadCdRomSub = ReadCddaSubMMC12;
+ return ReadCddaMMC12(usalp, p, lSector, SectorBurstVal);
+ }
+ usalp->silent--;
+ return retval;
+}
+
+/* Read the Sub-Q-Channel to SubQbuffer. This is the method for
+ * drives that do not support subchannel parameters. */
+#ifdef PROTOTYPES
+static subq_chnl *ReadSubQFallback (SCSI *usalp, unsigned char sq_format, unsigned char track)
+#else
+static subq_chnl *
+ReadSubQFallback(SCSI *usalp, unsigned char sq_format, unsigned char track)
+#endif
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)SubQbuffer;
+ scmd->size = 24;
+ 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; /* Read SubQChannel */
+ /* use LBA */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 0x40; /* SubQ info */
+ scmd->cdb.g1_cdb.addr[1] = 0; /* parameter list: all */
+ scmd->cdb.g1_cdb.res6 = track; /* track number */
+ g1_cdblen(&scmd->cdb.g1_cdb, 24);
+
+ if (usalp->verbose) fprintf(stderr, "\nRead Subchannel_dumb...");
+
+ usalp->cmdname = "Read Subchannel_dumb";
+
+ if (usal_cmd(usalp) < 0) {
+ fprintf( stderr, "Read SubQ failed\n");
+ }
+
+ /* check, if the requested format is delivered */
+ { unsigned char *p = (unsigned char *) SubQbuffer;
+ if ((((unsigned)p[2] << 8) | p[3]) /* LENGTH */ > ULONG_C(11) &&
+ (p[5] >> 4) /* ADR */ == sq_format) {
+ if (sq_format == GET_POSITIONDATA)
+ p[5] = (p[5] << 4) | (p[5] >> 4);
+ return SubQbuffer;
+ }
+ }
+
+ /* FIXME: we might actively search for the requested info ... */
+ return NULL;
+}
+
+/* Read the Sub-Q-Channel to SubQbuffer */
+#ifdef PROTOTYPES
+subq_chnl *ReadSubQSCSI (SCSI *usalp, unsigned char sq_format, unsigned char track)
+#else
+subq_chnl *
+ReadSubQSCSI(SCSI *usalp, unsigned char sq_format, unsigned char track)
+#endif
+{
+ int resp_size;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ switch (sq_format) {
+ case GET_POSITIONDATA:
+ resp_size = 16;
+ track = 0;
+ break;
+ case GET_CATALOGNUMBER:
+ resp_size = 24;
+ track = 0;
+ break;
+ case GET_TRACK_ISRC:
+ resp_size = 24;
+ break;
+ default:
+ fprintf(stderr, "ReadSubQSCSI: unknown format %d\n", sq_format);
+ return NULL;
+ }
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)SubQbuffer;
+ scmd->size = resp_size;
+ 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;
+ /* use LBA */
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 0x40; /* SubQ info */
+ scmd->cdb.g1_cdb.addr[1] = sq_format; /* parameter list: all */
+ scmd->cdb.g1_cdb.res6 = track; /* track number */
+ g1_cdblen(&scmd->cdb.g1_cdb, resp_size);
+
+ if (usalp->verbose) fprintf(stderr, "\nRead Subchannel...");
+
+ usalp->cmdname = "Read Subchannel";
+
+ if (usal_cmd(usalp) < 0) {
+ /* in case of error do a fallback for dumb firmwares */
+ return ReadSubQFallback(usalp, sq_format, track);
+ }
+
+ if (sq_format == GET_POSITIONDATA)
+ SubQbuffer->control_adr = (SubQbuffer->control_adr << 4) | (SubQbuffer->control_adr >> 4);
+ return SubQbuffer;
+}
+
+static subq_chnl sc;
+
+static subq_chnl* fill_subchannel(unsigned char bufferwithQ[]);
+static subq_chnl* fill_subchannel(unsigned char bufferwithQ[])
+{
+ sc.subq_length = 0;
+ sc.control_adr = bufferwithQ[CD_FRAMESIZE_RAW + 0];
+ sc.track = bufferwithQ[CD_FRAMESIZE_RAW + 1];
+ sc.index = bufferwithQ[CD_FRAMESIZE_RAW + 2];
+ return &sc;
+}
+
+int
+ReadCddaSubSony(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*(CD_FRAMESIZE_RAW + 16);
+ 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 = 0xd8; /* read audio command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ scmd->cdb.g5_cdb.res10 = 0x01; /* subcode 1 -> cdda + 16 * q sub */
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+
+ if (usalp->verbose) fprintf(stderr, "\nReadSony12 CDDA + SubChannels...");
+
+ usalp->cmdname = "Read12SubChannelsSony";
+
+ if (usal_cmd(usalp)) return -1;
+
+ /* has all or something been read? */
+ return usal_getresid(usalp) != 0;
+}
+
+int ReadCddaSub96Sony(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal);
+
+int ReadCddaSub96Sony(SCSI *usalp, UINT4 *p, unsigned lSector,
+ unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*(CD_FRAMESIZE_RAW + 96);
+ 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 = 0xd8; /* read audio command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res |= (accepts_fua_bit == 1 ? 1 << 2 : 0);
+ scmd->cdb.g5_cdb.res10 = 0x02; /* subcode 2 -> cdda + 96 * q sub */
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+
+ if (usalp->verbose) fprintf(stderr, "\nReadSony12 CDDA + 96 byte SubChannels...");
+
+ usalp->cmdname = "Read12SubChannelsSony";
+
+ if (usal_cmd(usalp)) return -1;
+
+ /* has all or something been read? */
+ return usal_getresid(usalp) != 0;
+}
+
+subq_chnl *ReadSubChannelsSony(SCSI *usalp, unsigned lSector)
+{
+ /*int retval = ReadCddaSub96Sony(usalp, (UINT4 *)bufferTOC, lSector, 1);*/
+ int retval = ReadCddaSubSony(usalp, (UINT4 *)bufferTOC, lSector, 1);
+ if (retval != 0) return NULL;
+
+ return fill_subchannel(bufferTOC);
+}
+
+/* Read max. SectorBurst of cdda sectors to buffer
+ via MMC standard READ CD command */
+int ReadCddaSubMMC12(SCSI *usalp, UINT4 *p, unsigned lSector, unsigned SectorBurstVal)
+{
+ register struct usal_cmd *scmd;
+ scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)p;
+ scmd->size = SectorBurstVal*(CD_FRAMESIZE_RAW + 16);
+ 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 = 0xbe; /* read cd command */
+ scmd->cdb.g5_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g5_cdb.res = 1 << 1; /* expected sector type field CDDA */
+ g5_cdbaddr(&scmd->cdb.g5_cdb, lSector);
+ g5x_cdblen(&scmd->cdb.g5_cdb, SectorBurstVal);
+ scmd->cdb.g5_cdb.count[3] = 1 << 4; /* User data */
+ scmd->cdb.g5_cdb.res10 = 0x02; /* subcode 2 -> cdda + 16 * q sub */
+
+ if (usalp->verbose) fprintf(stderr, "\nReadMMC12 CDDA + SUB...");
+
+ usalp->cmdname = "ReadCD Sub MMC 12";
+
+ if (usal_cmd(usalp)) return -1;
+
+ /* has all or something been read? */
+ return usal_getresid(usalp) != 0;
+}
+
+static subq_chnl *ReadSubChannelsMMC(SCSI *usalp, unsigned lSector);
+static subq_chnl *ReadSubChannelsMMC(SCSI *usalp, unsigned lSector)
+{
+ int retval = ReadCddaSubMMC12(usalp, (UINT4 *)bufferTOC, lSector, 1);
+ if (retval != 0) return NULL;
+
+ return fill_subchannel(bufferTOC);
+}
+
+subq_chnl *ReadSubChannelsFallbackMMC(SCSI *usalp, unsigned lSector)
+{
+ static int ReadSubSony_unknown = 0;
+ subq_chnl *retval = NULL;
+
+ usalp->silent++;
+ if (ReadSubSony_unknown
+ || ((retval = ReadSubChannelsSony(usalp, lSector)) == NULL)) {
+ /* if the command is not available, use the regular
+ * MMC ReadCd
+ */
+ if (retval == NULL && usal_sense_key(usalp) == 0x05) {
+ ReadSubSony_unknown = 1;
+ }
+ usalp->silent--;
+ return ReadSubChannelsMMC(usalp, lSector);
+ }
+ usalp->silent--;
+ return retval;
+}
+
+subq_chnl *ReadStandardSub(usalp, lSector)
+ SCSI *usalp;
+ unsigned lSector;
+{
+ if (0 == ReadStandardLowlevel (usalp, (UINT4 *)bufferTOC, lSector, 1, CD_FRAMESIZE_RAW + 16 )) {
+ return NULL;
+ }
+#if 0
+fprintf(stderr, "Subchannel Sec %x: %02x %02x %02x %02x\n"
+ ,lSector
+ ,bufferTOC[CD_FRAMESIZE_RAW + 0]
+ ,bufferTOC[CD_FRAMESIZE_RAW + 1]
+ ,bufferTOC[CD_FRAMESIZE_RAW + 2]
+ ,bufferTOC[CD_FRAMESIZE_RAW + 3]
+ );
+#endif
+ sc.control_adr = (bufferTOC[CD_FRAMESIZE_RAW + 0] << 4)
+ | bufferTOC[CD_FRAMESIZE_RAW + 1];
+ sc.track = from_bcd(bufferTOC[CD_FRAMESIZE_RAW + 2]);
+ sc.index = from_bcd(bufferTOC[CD_FRAMESIZE_RAW + 3]);
+ return &sc;
+}
+/********* non standardized speed selects ***********************/
+
+void SpeedSelectSCSIToshiba(SCSI *usalp, unsigned speed)
+{
+ static unsigned char mode [4 + 3];
+ unsigned char *page = mode + 4;
+ int retval;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ /* the first 4 mode bytes are zero. */
+ page[0] = 0x20;
+ page[1] = 1;
+ page[2] = speed; /* 0 for single speed, 1 for double speed (3401) */
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select Toshiba...");
+
+ usalp->silent++;
+ /* do the scsi cmd */
+ if ((retval = mode_select(usalp, mode, 7, 0, usalp->inq->data_format >= 2)) < 0)
+ fprintf (stderr, "speed select Toshiba failed\n");
+ usalp->silent--;
+}
+
+void SpeedSelectSCSINEC(SCSI *usalp, unsigned speed)
+{
+ static unsigned char mode [4 + 8];
+ unsigned char *page = mode + 4;
+ int retval;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ /* the first 4 mode bytes are zero. */
+ page [0] = 0x0f; /* page code */
+ page [1] = 6; /* parameter length */
+ /* bit 5 == 1 for single speed, otherwise double speed */
+ page [2] = speed == 1 ? 1 << 5 : 0;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)mode;
+ scmd->size = 12;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0xC5;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[0] = 0 ? 1 : 0 | 1 ? 0x10 : 0;
+ g1_cdblen(&scmd->cdb.g1_cdb, 12);
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select NEC...");
+ /* do the scsi cmd */
+
+ usalp->cmdname = "speed select NEC";
+
+ if ((retval = usal_cmd(usalp)) < 0)
+ fprintf(stderr ,"speed select NEC failed\n");
+}
+
+void SpeedSelectSCSIPhilipsCDD2600(SCSI *usalp, unsigned speed)
+{
+ /* MODE_SELECT, page = SCSI-2 save page disabled, reserved, reserved,
+ parm list len, flags */
+ static unsigned char mode [4 + 8];
+ unsigned char *page = mode + 4;
+ int retval;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ /* the first 4 mode bytes are zero. */
+ page[0] = 0x23;
+ page[1] = 6;
+ page[2] = page [4] = speed;
+ page[3] = 1;
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select Philips...");
+ /* do the scsi cmd */
+ if ((retval = mode_select(usalp, mode, 12, 0, usalp->inq->data_format >= 2)) < 0)
+ fprintf (stderr, "speed select PhilipsCDD2600 failed\n");
+}
+
+void SpeedSelectSCSISony(SCSI *usalp, unsigned speed)
+{
+ static unsigned char mode [4 + 4];
+ unsigned char *page = mode + 4;
+ int retval;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ /* the first 4 mode bytes are zero. */
+ page[0] = 0x31;
+ page[1] = 2;
+ page[2] = speed;
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select Sony...");
+ /* do the scsi cmd */
+ usalp->silent++;
+ if ((retval = mode_select(usalp, mode, 8, 0, usalp->inq->data_format >= 2)) < 0)
+ fprintf (stderr, "speed select Sony failed\n");
+ usalp->silent--;
+}
+
+void SpeedSelectSCSIYamaha (usalp, speed)
+ SCSI *usalp;
+ unsigned speed;
+{
+ static unsigned char mode [4 + 4];
+ unsigned char *page = mode + 4;
+ int retval;
+
+ fillbytes((caddr_t)mode, sizeof(mode), '\0');
+ /* the first 4 mode bytes are zero. */
+ page[0] = 0x31;
+ page[1] = 2;
+ page[2] = speed;
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select Yamaha...");
+ /* do the scsi cmd */
+ if ((retval = mode_select(usalp, mode, 8, 0, usalp->inq->data_format >= 2)) < 0)
+ fprintf (stderr, "speed select Yamaha failed\n");
+}
+
+void SpeedSelectSCSIMMC(SCSI *usalp, unsigned speed)
+{
+ int spd;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (speed == 0 || speed == 0xFFFF) {
+ spd = 0xFFFF;
+ } else {
+ spd = (1764 * speed) / 10;
+ }
+ 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);
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], spd);
+ i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xffff);
+
+ if (usalp->verbose) fprintf(stderr, "\nspeed select MMC...");
+
+ usalp->cmdname = "set cd speed";
+
+ usalp->silent++;
+ if (usal_cmd(usalp) < 0) {
+ if (usal_sense_key(usalp) == 0x05 &&
+ usal_sense_code(usalp) == 0x20 &&
+ usal_sense_qual(usalp) == 0x00) {
+ /* this optional command is not implemented */
+ } else {
+ usal_printerr(usalp);
+ fprintf (stderr, "speed select MMC failed\n");
+ }
+ }
+ usalp->silent--;
+}
+
+/* request vendor brand and model */
+unsigned char *Inquiry(SCSI *usalp)
+{
+ static unsigned char *Inqbuffer = NULL;
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (Inqbuffer == NULL) {
+ Inqbuffer = malloc(36);
+ if (Inqbuffer == NULL) {
+ fprintf(stderr, "Cannot allocate memory for inquiry command in line %d\n", __LINE__);
+ return NULL;
+ }
+ }
+
+ fillbytes(Inqbuffer, 36, '\0');
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)Inqbuffer;
+ scmd->size = 36;
+ 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 = 36;
+
+ usalp->cmdname = "inquiry";
+
+ if (usal_cmd(usalp) < 0)
+ return (NULL);
+
+ /* define structure with inquiry data */
+ memcpy(usalp->inq, Inqbuffer, sizeof(*usalp->inq));
+
+ if (usalp->verbose)
+ usal_prbytes("Inquiry Data :", (Uchar *)Inqbuffer, 22 - scmd->resid);
+
+ return (Inqbuffer);
+}
+
+#define SC_CLASS_EXTENDED_SENSE 0x07
+#define TESTUNITREADY_CMD 0
+#define TESTUNITREADY_CMDLEN 6
+
+#define ADD_SENSECODE 12
+#define ADD_SC_QUALIFIER 13
+#define NO_MEDIA_SC 0x3a
+#define NO_MEDIA_SCQ 0x00
+
+int TestForMedium(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ if (interface != GENERIC_SCSI) {
+ return 1;
+ }
+
+ /* request READY status */
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = (caddr_t)0;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA | (1 ? 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);
+
+ if (usalp->verbose) fprintf(stderr, "\ntest unit ready...");
+ usalp->silent++;
+
+ usalp->cmdname = "test unit ready";
+
+ if (usal_cmd(usalp) >= 0) {
+ usalp->silent--;
+ return 1;
+ }
+ usalp->silent--;
+
+ if (scmd->sense.code >= SC_CLASS_EXTENDED_SENSE) {
+ return
+ scmd->u_sense.cmd_sense[ADD_SENSECODE] != NO_MEDIA_SC ||
+ scmd->u_sense.cmd_sense[ADD_SC_QUALIFIER] != NO_MEDIA_SCQ;
+ } else {
+ /* analyse status. */
+ /* 'check condition' is interpreted as not ready. */
+ return (scmd->u_scb.cmd_scb[0] & 0x1e) != 0x02;
+ }
+}
+
+int StopPlaySCSI(SCSI *usalp)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = NULL;
+ 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 = 0x1b;
+ scmd->cdb.g0_cdb.lun = usal_lun(usalp);
+
+ if (usalp->verbose) fprintf(stderr, "\nstop audio play");
+ /* do the scsi cmd */
+
+ usalp->cmdname = "stop audio play";
+
+ return usal_cmd(usalp) >= 0 ? 0 : -1;
+}
+
+int Play_atSCSI(SCSI *usalp, unsigned int from_sector, unsigned int sectors)
+{
+ register struct usal_cmd *scmd = usalp->scmd;
+
+ fillbytes((caddr_t)scmd, sizeof(*scmd), '\0');
+ scmd->addr = NULL;
+ scmd->size = 0;
+ scmd->flags = SCG_DISRE_ENA;
+ scmd->cdb_len = SC_G1_CDBLEN;
+ scmd->sense_len = CCS_SENSE_LEN;
+ scmd->cdb.g1_cdb.cmd = 0x47;
+ scmd->cdb.g1_cdb.lun = usal_lun(usalp);
+ scmd->cdb.g1_cdb.addr[1] = (from_sector + 150) / (60*75);
+ scmd->cdb.g1_cdb.addr[2] = ((from_sector + 150) / 75) % 60;
+ scmd->cdb.g1_cdb.addr[3] = (from_sector + 150) % 75;
+ scmd->cdb.g1_cdb.res6 = (from_sector + 150 + sectors) / (60*75);
+ scmd->cdb.g1_cdb.count[0] = ((from_sector + 150 + sectors) / 75) % 60;
+ scmd->cdb.g1_cdb.count[1] = (from_sector + 150 + sectors) % 75;
+
+ if (usalp->verbose) fprintf(stderr, "\nplay sectors...");
+ /* do the scsi cmd */
+
+ usalp->cmdname = "play sectors";
+
+ return usal_cmd(usalp) >= 0 ? 0 : -1;
+}
+
+static caddr_t scsibuffer; /* page aligned scsi transfer buffer */
+
+void init_scsibuf(SCSI *scsp, unsigned amt);
+
+void init_scsibuf(SCSI *usalp, unsigned amt)
+{
+ if (scsibuffer != NULL) {
+ fprintf(stderr, "the SCSI transfer buffer has already been allocated!\n");
+ exit(SETUPSCSI_ERROR);
+ }
+ scsibuffer = usal_getbuf(usalp, amt);
+ if (scsibuffer == NULL) {
+ fprintf(stderr, "could not get SCSI transfer buffer!\n");
+ exit(SETUPSCSI_ERROR);
+ }
+}