diff options
Diffstat (limited to 'icedax/scsi_cmds.c')
-rw-r--r-- | icedax/scsi_cmds.c | 1720 |
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 ≻ +} + +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 ≻ +} +/********* 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); + } +} |