/* file for all SCSI commands */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <btorder.h>
#define g5_cdbaddr(a,b) g1_cdbaddr(a,b)
#define        g5_cdblen(cdb, len)     ((cdb)->count[0] = (len) >> 24L,\
                                (cdb)->count[1] = ((len) >> 16L)& 0xFF,\
                                (cdb)->count[2] = ((len) >> 8L) & 0xFF,\
                                (cdb)->count[3] = (len) & 0xFF)

#define        g5x_cdblen(cdb, len)    ((cdb)->count[0] = ((len) >> 16L)& 0xFF,\
                                (cdb)->count[1] = ((len) >> 8L) & 0xFF,\
                                (cdb)->count[2] = (len) & 0xFF)



#include <scgio.h>
#include <scsidefs.h>
#include <scsireg.h>
#include <scsitransp.h>

#include "readall.h"
#include "interface.h"
#include "byteorder.h"
#include "global.h"

unsigned char *bufferTOC;
unsigned char *cmd;
int adjust_ssize;

static int
mode_sense_g0(unsigned char *dp, int cnt, int page, int pcf)
{
        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)dp;
        scmd.size = cnt;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G0_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g0_cdb.cmd = SC_MODE_SENSE;
        scmd.cdb.g0_cdb.lun = global.lun;
#ifdef  nonono
        scmd.cdb.g0_cdb.high_addr = 1<<4;       /* DBD Disable Block desc. */
#endif
        scmd.cdb.g0_cdb.mid_addr = (page&0x3F) | ((pcf<<6)&0xC0);
        scmd.cdb.g0_cdb.count = cnt;

        if (scsicmd("mode sense g0") < 0)
                return (-1);
        if (jes_verbose) scsiprbytes("Mode Sense Data", dp, cnt - scmd.resid);
        return (0);
}

static int
mode_sense_g1(unsigned char *dp, int cnt, int page, int pcf)
{
        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)dp;
        scmd.size = cnt;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x5A;
        scmd.cdb.g1_cdb.lun = global.lun;
#ifdef  nonono
        scmd.cdb.g0_cdb.high_addr = 1<<4;       /* DBD Disable Block desc. */
#endif
        scmd.cdb.g1_cdb.addr[0] = (page&0x3F) | ((pcf<<6)&0xC0);
        g1_cdblen(&scmd.cdb.g1_cdb, cnt);

        if (scsicmd("mode sense g1") < 0)
                return (-1);
        if (jes_verbose) scsiprbytes("Mode Sense Data", dp, cnt - scmd.resid);
        return (0);
}

static int
mode_select_g1(unsigned char *dp, int cnt, int smp, int pf)
{
        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)dp;
        scmd.size = cnt;
        scmd.flags = SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x55;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
        g1_cdblen(&scmd.cdb.g1_cdb, cnt);

        if (jes_verbose) {
                fprintf(stderr, "%s ", smp?"Save":"Set ");
                scsiprbytes("Mode Parameters", dp, cnt);
        }

        return (scsicmd("mode select g1"));
}

static int
mode_select_g0(unsigned char *dp, int cnt, int smp, int pf)
{
	fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)dp;
        scmd.size = cnt;
        scmd.flags = SCG_DISRE_ENA;
        scmd.cdb_len = SC_G0_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g0_cdb.cmd = SC_MODE_SELECT;
        scmd.cdb.g0_cdb.lun = global.lun;
        scmd.cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0;
        scmd.cdb.g0_cdb.count = cnt;

        if (jes_verbose) {
                fprintf(stderr, "%s ", smp?"Save":"Set ");
                scsiprbytes("Mode Parameters", dp, cnt);
        }

        return (scsicmd("mode select g0"));
}

static int      is_atapi = 0;

int SCSI_emulated_ATAPI_on(void) {
	return is_atapi;
}

int allow_atapi(int new)
{
        int     old = is_atapi;
        unsigned char  mode[256];

        if (new == old)
                return (old);

        jes_silent++;
	if (new && jes_verbose) fprintf(stderr, "\nallow atapi: get all mode pages ...");
        if (new &&
            mode_sense_g1(mode, 8, 0x3F, 0) < 0) {      /* All pages current */
                new = FALSE;
        }
        jes_silent--;

        is_atapi = new;
        return (old);
}

static int
mode_sense_sg0(unsigned char *dp, int cnt, int page, int pcf)
{
        u_char  xmode[256+4];
        int     amt = cnt;
        int     len;

        fillbytes((caddr_t)xmode, sizeof(scmd), '\0');
        if (amt < 4) {          /* Data length. medium type & VU */
                amt += 1;
        } else {
                amt += 4;
        }
        if (mode_sense_g1(xmode, amt, page, pcf) < 0)
                return (-1);

        cnt -= scsigetresid();
        movebytes(&xmode[8], &dp[4], cnt-4);
        len = a_to_u_short(xmode);
        if (len == 0) {
                dp[0] = 0;
        } else if (len < 6) {
                if (len > 2)
                        len = 2;
                dp[0] = len;
        } else {
                dp[0] = len - 3;
        }
        dp[1] = xmode[2];
        dp[2] = xmode[3];
        len = a_to_u_short(&xmode[6]);
        dp[3] = len;

        if (jes_verbose) scsiprbytes("Mode Sense Data (converted)", dp, cnt);
        return (0);
}

static int
mode_select_sg0(unsigned char *dp, int cnt, int smp, int pf)
{
        u_char  xmode[256+4];
        int     amt = cnt;

        movebytes(&dp[4], &xmode[8], cnt-4);
        if (amt < 4) {          /* Data length. medium type & VU */
                amt += 1;
        } else {
                amt += 4;
        }
        xmode[0] = 0;
        xmode[1] = 0;
        xmode[2] = dp[1];
        xmode[3] = dp[2];
        xmode[4] = 0;
        xmode[5] = 0;
        i_to_short(&xmode[6], dp[3]);

        if (jes_verbose) scsiprbytes("Mode Parameters (un-converted)", dp, cnt);

        return (mode_select_g1(xmode, amt, smp, pf));
}

static int
mode_select(unsigned char *dp, int cnt, int smp, int pf)
{
        if (is_atapi)
                return (mode_select_sg0(dp, cnt, smp, pf));
        return (mode_select_g0(dp, cnt, smp, pf));
}

static int
mode_sense(unsigned char *dp, int cnt, int page, int pcf)
{
        if (is_atapi)
                return (mode_sense_sg0(dp, cnt, page, pcf));
        return (mode_sense_g0(dp, cnt, page, pcf));
}

int test_unit_ready(void)
{
	fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
	scmd.addr = (caddr_t)0;
	scmd.size = 0;
	scmd.flags = SCG_DISRE_ENA | (jes_silent ? SCG_SILENT:0);
	scmd.cdb_len = SC_G0_CDBLEN;
	scmd.sense_len = CCS_SENSE_LEN;
	scmd.target = target;
	scmd.cdb.g0_cdb.cmd = SC_TEST_UNIT_READY;
	scmd.cdb.g0_cdb.lun = lun;
	
	return (scsicmd("test unit ready"));
}

#define scsi_compliant 0
#define WITH_MODE_PAGE_DETECT
#ifdef WITH_MODE_PAGE_DETECT
static int has_mode_page(int page, const char *pagename, int *lenp)
{
	u_char	mode[0x100];
	int	len = 1;				/* Nach SCSI Norm */
	int	try = 0;
	struct	scsi_mode_page_header *mp;

again:
	fillbytes((caddr_t)mode, sizeof(mode), '\0');
	if (lenp)
		*lenp = 0;

	(void)test_unit_ready();
	jes_silent++;
/* Maxoptix bringt Aborted cmd 0x0B mit code 0x4E (overlapping cmds)*/

	/*
	 * The Matsushita CW-7502 will sometimes deliver a zeroed
	 * mode page 2A if "Page n default" is used instead of "current".
	 */
	if (mode_sense(mode, len, page, 0) < 0) {	/* Page n current */
		jes_silent--;
		return (FALSE);
	} else {
		len = ((struct scsi_mode_header *)mode)->sense_data_len + 1;
	}
	if (mode_sense(mode, len, page, 0) < 0) {	/* Page n current */
		jes_silent--;
		return (FALSE);
	}
	jes_silent--;

	if (jes_verbose)
		scsiprbytes("Mode Sense Data", mode, len - scsigetresid());
	mp = (struct scsi_mode_page_header *)
		(mode + sizeof(struct scsi_mode_header) +
		((struct scsi_mode_header *)mode)->blockdesc_len);
	if (jes_verbose)
		scsiprbytes("Mode Sense Data", (u_char *)mp, mp->p_len+2);

	if (mp->p_len == 0) {
		if (!scsi_compliant && try == 0) {
			len = sizeof(struct scsi_mode_header) +
			((struct scsi_mode_header *)mode)->blockdesc_len;
			/*
			 * add sizeof page header (page # + len byte)
			 * (should normaly result in len == 14)
			 * this allowes to work with:
			 * 	Quantum Q210S	(wants at least 13)
			 * 	MD2x		(wants at least 4)
			 */
			len += 2;
			try++;
			goto again;
		}
		errmsgno(EX_BAD,
			"Warning: controller returns zero sized %s page.\n",
								pagename);
	}

	if (lenp)
		*lenp = len;
	return (mp->p_len > 0);
}
#endif

static int get_mode_params(int page, const char *pagename, unsigned char *modep,
	unsigned char *cmodep, unsigned char *dmodep, unsigned char *smodep,
	int *lenp)
{
        int     len = -1;
        int     ret = TRUE;

#ifdef WITH_MODE_PAGE_DETECT
        if (lenp)
                *lenp = 0;
        if (!has_mode_page(page, pagename, &len)) {
                if (!jes_silent) errmsgno(EX_BAD,
                        "Warning: controller does not support %s page.\n",
                                                                pagename);
                return (1);  /* Hopefully we can continue */
        }
        if (lenp)
                *lenp = len;
#else
        if (lenp == NULL)
                len = 0xFF;
#endif

        if (modep) {
                fillbytes(modep, 0x100, '\0');
                if (mode_sense(modep, len, page, 0) < 0) {/* Page x current */
                        errmsgno(EX_BAD, "Cannot get %s data.\n", pagename);
                        ret = 0;
                } else if (jes_verbose) {
                        scsiprbytes("Mode Sense Data", modep, len - scsigetresid());
                }
        }

        if (cmodep) {
                fillbytes(cmodep, 0x100, '\0');
                if (mode_sense(cmodep, len, page, 1) < 0) {/* Page x change */
                        errmsgno(EX_BAD, "Cannot get %s mask.\n", pagename);
                        ret = 0;
                } else if (jes_verbose) {
                        scsiprbytes("Mode Sense Data", cmodep, len - scsigetresid());
                }
        }

        if (dmodep) {
                fillbytes(dmodep, 0x100, '\0');
                if (mode_sense(dmodep, len, page, 2) < 0) {/* Page x default */
                        errmsgno(EX_BAD, "Cannot get default %s data.\n",
                                                                pagename);
                        ret = 0;
                } else if (jes_verbose) {
                        scsiprbytes("Mode Sense Data", dmodep, len - scsigetresid());
                }
        }

        if (smodep) {
                fillbytes(smodep, 0x100, '\0');
                if (mode_sense(smodep, len, page, 3) < 0) {/* Page x saved */
                        errmsgno(EX_BAD, "Cannot get saved %s data.\n", pagename);
                        ret = 0;
                } else if (jes_verbose) {
                        scsiprbytes("Mode Sense Data", smodep, len - scsigetresid());
                }
        }

        return (ret);
}

static struct cd_mode_page_2A *
mmc_cap(unsigned char *modep)
{
        int     len;
        int     val;
        unsigned char	mode[0x100];
        struct  cd_mode_page_2A *mp;
        struct  cd_mode_page_2A *mp2;

        fillbytes((caddr_t)mode, sizeof(mode), '\0');

	if (jes_verbose) fprintf(stderr, "\nget mmc capabilities...");
        if (!get_mode_params(0x2A, "CD capabilities",
              mode, (unsigned char *)0, (unsigned char *)0, (unsigned char *)0, &len))
                return (NULL);

        if (len == 0)                   /* Pre SCSI-3/mmc drive         */
                return (NULL);

        mp = (struct cd_mode_page_2A *)
                (mode + sizeof(struct scsi_mode_header) +
                ((struct scsi_mode_header *)mode)->blockdesc_len);
        /*
         * Do some heuristics against pre SCSI-3/mmc VU page 2A
         */
        if (mp->p_len < 0x14)
                return (NULL);

        val = a_to_u_short(mp->max_read_speed);
        if (val != 0 && val < 176)
                return (NULL);

        val = a_to_u_short(mp->cur_read_speed);
        if (val != 0 && val < 176)
                return (NULL);

        len -= sizeof(struct scsi_mode_header) +
                ((struct scsi_mode_header *)mode)->blockdesc_len;
        if (modep)
                mp2 = (struct cd_mode_page_2A *)modep;
        else
                mp2 = malloc(len);
        if (mp2)
                movebytes(mp, mp2, len);

        return (mp2);
}


int is_mmc(void)
{
        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(1);
        jes_silent++;
        mp = mmc_cap(mode);
        jes_silent--;
        allow_atapi(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;
}

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(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 (jes_verbose) fprintf(stderr, "\nget density and sector size...");
      if (mode_sense(modesense, 12, 0x01, 0) < 0)
	  perror ("get_orig_sectorsize mode sense failed\n");

      /* FIXME: some drives dont deliver block descriptors !!! */
      if (modesense[3] == 0)
        return 0;

      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 (unsigned int secsize, int set_density)
{
  /* 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];
  int retval;

  if (orgmode4 == 0xff) {
    get_orig_sectorsize(&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 (jes_verbose) fprintf(stderr, "\nset density and sector size...");
  /* do the scsi cmd */
  if ((retval = mode_select(mode, 12, 0, 0)) < 0)
        perror ("setting sector size failed\n");

  return retval;
}


/* switch Toshiba/DEC and HP drives from/to cdda density */
void EnableCddaModeSelect (int fAudioMode)
{
  /* 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(&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] = (CD_FRAMESIZE_RAWER >> 8L);   /* block length "msb" */
    mode [11] = (CD_FRAMESIZE_RAWER & 0xFF);
  } 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 (jes_verbose) fprintf(stderr, "\nset density and sector size (EnableCddaModeSelect)...\n");
  /* do the scsi cmd */
  if (mode_select(mode, 12, 0, 0) < 0)
        perror ("Audio mode switch failed\n");
}

#define DEBUG_CDTEXT

typedef struct {
unsigned char headerfield[4];
unsigned char textdatafield[12];
unsigned char crcfield[2];
} cdtextpackdata;

/* read CD Text information from the table of contents */
void ReadTocTextSCSI ( void )
{
    short int datalength;
    unsigned char *p = bufferTOC;

  /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
     len lsb, control */

        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.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.addr[0] = 5;
        scmd.cdb.g1_cdb.res6 = 1;
        g1_cdblen(&scmd.cdb.g1_cdb, 4);

        jes_silent++;
        if (jes_verbose) fprintf(stderr, "\nRead TOC CD Text size ...");
        if (scsicmd("read toc size (text)") < 0) {
          jes_silent--;
	  if (global.quiet != 1)
            fprintf (stderr, "Read TOC CD Text failed (probably not supported).\n");
          return ;
        }
        jes_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.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.addr[0] = 5;
        scmd.cdb.g1_cdb.res6 = 1;
        g1_cdblen(&scmd.cdb.g1_cdb, 2+datalength);

        jes_silent++;
        if (jes_verbose) fprintf(stderr, "\nRead TOC CD Text data ...");
        if (scsicmd("read toc data (text)") < 0) {
          jes_silent--;
	  if (global.quiet != 1)
            fprintf (stderr,  "Read TOC CD Text data failed (probably not supported).\n");
          return ;
        }
        jes_silent--;

  /* interpret the contents of CD Text information based on an early draft
     of SCSI-3 mmc version 2 from jan 2, 1998
     CD Text information consists of a text group containing up to
     8 language blocks containing up to
     255 Pack data chunks of
     18 bytes each.
     So we have at most 36720 bytes to cope with.
   */
  {
    datalength -= 2;
    p += 4;
    while (datalength > 0) {
      /* handle one packet of CD Text Information Descriptor Pack Data */
      /* this is raw R-W subchannel data converted to 8 bit values. */
      cdtextpackdata *c = (cdtextpackdata *)p;
      int bin_print=1;
      int extension_flag;
      int sequence_number;
      int dbcc;
      int block_number;
      int character_position;

      datalength -= sizeof (cdtextpackdata);
      p += sizeof (cdtextpackdata);

      extension_flag = (c->headerfield[1] & 0x80) >> 7;
      c->headerfield[1] &= 0x7f;
      sequence_number = c->headerfield[2];
      dbcc = (c->headerfield[3] & 0x80) >> 7; /* double byte character code */
      block_number = (c->headerfield[3] & 0x30) >> 4; /* language */
      character_position = c->headerfield[3] & 0x0f;
#if defined DEBUG_CDTEXT
      fprintf(stderr, "CDText: ext_fl=%d, seq_nr=%d, dbcc=%d, block_nr=%d, char_pos=%d\n",
             extension_flag, sequence_number, dbcc, block_number, character_position);
#endif
      switch (c->headerfield[0]) {

        case 0x80: /* Title of album or track */
          if (c->headerfield[1] == 0) {
            fprintf (stderr, "album title: ");
          } else {
            fprintf(stderr, "title for track %u: ", c->headerfield[1]);
          }
          bin_print = 0;
        break;
        case 0x81: /* Name(s) of the performer(s) */
          fprintf(stderr, "Performer(s): ");
          bin_print = 0;
        break;
        case 0x82: /* Name(s) of the songwriter(s) */
          fprintf(stderr, "Songwriter(s): ");
          bin_print = 0;
        break;
        case 0x83: /* Name(s) of the composer(s) */
          fprintf(stderr, "Composer(s): ");
          bin_print = 0;
        break;
        case 0x84: /* Name(s) of the arranger(s) */
          fprintf(stderr, "Arranger(s): ");
          bin_print = 0;
        break;
        case 0x85: /* Message from content provider and/or artist */
          fprintf(stderr, "Message: ");
          bin_print = 0;
        break;
        case 0x86: /* Disc Identification and information */
          fprintf(stderr, "Disc identification: ");
          bin_print = 1;
        break;
        case 0x87: /* Genre Identification and information */
          fprintf(stderr, "Genre identification: ");
          bin_print = 1;
        break;
        case 0x88: /* Table of Content information */
          fprintf(stderr, "Table of Content identification: ");
          bin_print = 1;
        break;
        case 0x89: /* Second Table of Content information */
          fprintf(stderr, "Second Table of Content identification: ");
          bin_print = 1;
        break;
        case 0x8e: /* UPC/EAN code or ISRC code */
          fprintf(stderr, "UPC or ISRC: ");
          bin_print = 1;
        break;
        case 0x8f: /* Size information of the block */
          fprintf(stderr, "size of block: ");
          bin_print = 1;
        break;
#if !defined DEBUG_CDTEXT
        default:
#else
      }
#endif
          fprintf(stderr, "Reserved Pack Type Indicators %02x %02x %02x\n",
                          c->headerfield[0], c->headerfield[1], c->headerfield[2]);
#if !defined DEBUG_CDTEXT
          bin_print = 1;
      }
      if (dbcc != 0 || bin_print != 0) {
#else
      {
#endif
          fprintf(stderr, "%02x %02x %02x %02x %02x %02x "
                          "%02x %02x %02x %02x %02x %02x\n"
                        , c->textdatafield[0]
                        , c->textdatafield[1]
                        , c->textdatafield[2]
                        , c->textdatafield[3]
                        , c->textdatafield[4]
                        , c->textdatafield[5]
                        , c->textdatafield[6]
                        , c->textdatafield[7]
                        , c->textdatafield[8]
                        , c->textdatafield[9]
                        , c->textdatafield[10]
                        , c->textdatafield[11]
                 );
      }
      if (dbcc == 0 && bin_print == 0) {
        /* print ASCII information */
        fprintf (stderr, "%12.12s\n", c->textdatafield);
      }
    }
  }
}

/* read the start of the lead-out from the first session TOC */
unsigned ReadFirstSessionTOCSony ( unsigned tracks )
{
    short int datalength;
    unsigned char *p = bufferTOC;

  /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
     len lsb, control */

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)bufferTOC;
        scmd.size = 4 + (tracks + 3) * 11;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.res = 1;
        scmd.cdb.g1_cdb.res6 = 1;    /* session */
        g1_cdblen(&scmd.cdb.g1_cdb, 4 + (tracks + 3) * 11);
        scmd.cdb.g1_cdb.vu_97 = 1;   /* format */

        jes_silent++;
        if (jes_verbose) fprintf(stderr, "\nRead TOC first session ...");
        if (scsicmd("read toc first session") < 0) {
          jes_silent--;
	  if (global.quiet != 1)
            fprintf (stderr, "Read TOC first session failed (probably not supported).\n");
          return 0U;
        }
        jes_silent--;

        if ((unsigned)((bufferTOC[0] << 8) | bufferTOC[1]) >= 4 + (tracks + 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 0U;
}

/* read the start of the lead-out from the first session TOC */
unsigned ReadFirstSessionTOCMMC ( unsigned tracks )
{
    short int datalength;
    unsigned char *p = bufferTOC;

  /* READTOC, MSF, format, res, res, res, Start track/session, len msb,
     len lsb, control */

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)bufferTOC;
        scmd.size = 4 + (tracks + 3) * 11;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.res = 1;
        scmd.cdb.g1_cdb.addr[0] = 2; /* format */
        scmd.cdb.g1_cdb.res6 = 1;    /* session */
        g1_cdblen(&scmd.cdb.g1_cdb, 4 + (tracks + 3) * 11);

        jes_silent++;
        if (jes_verbose) fprintf(stderr, "\nRead TOC first session ...");
        if (scsicmd("read toc first session") < 0) {
          jes_silent--;
	  if (global.quiet != 1)
            fprintf (stderr, "Read TOC first session failed (probably not supported).\n");
          return 0U;
        }
        jes_silent--;

        if ((unsigned)((bufferTOC[0] << 8) | bufferTOC[1]) >= 4 + (tracks + 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 + 3;
          while (off < 4 + (tracks + 3) * 11 && bufferTOC[off] != 0xA2) {
            off += 11;
          }
          if (off < 4 + (tracks + 3) * 11) {
            off += 5;
            return (bufferTOC[off]*60 + bufferTOC[off+1])*75 + bufferTOC[off+2] - 150;
          }
        }
        return 0U;
}

/* read the table of contents from the cd and fill the TOC array */
unsigned ReadTocSCSI ( TOC *toc )
{
  unsigned i;
  unsigned tracks;

  /* READTOC, MSF format flag, res, res, res, res, Start track, len msb,
     len lsb, flags */

        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.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.addr[0] = 0;
        scmd.cdb.g1_cdb.res6 = 1;
        g1_cdblen(&scmd.cdb.g1_cdb, 4);

        if (jes_verbose) fprintf(stderr, "\nRead TOC size (standard)...");
  /* do the scsi cmd (read table of contents) */
  if (scsicmd("read toc size") < 0)
      FatalError ("Read TOC size failed.\n");


  tracks = ((bufferTOC [3] ) - bufferTOC [2] + 2) ;
  if (tracks > MAXTRK) return 0;
  if (tracks == 0) return 0;


        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.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x43;
        scmd.cdb.g1_cdb.lun = global.lun;
        scmd.cdb.g1_cdb.addr[0] = 0;
        scmd.cdb.g1_cdb.res6 = 1;
        g1_cdblen(&scmd.cdb.g1_cdb, 4 + tracks * 8);

        if (jes_verbose) fprintf(stderr, "\nRead TOC tracks (standard)...");
  /* do the scsi cmd (read table of contents) */
  if (scsicmd("read toc tracks ") < 0)
      FatalError ("Read TOC tracks failed.\n");

  /* copy to our structure and convert start sector */
  for (i = 0; i < tracks; i++) {
      memcpy (&toc[i], bufferTOC + 4 + 8*i, 8);
      toc[i].ISRC[0] = 0;
      toc[i].dwStartSector = adjust_ssize * be32_to_cpu(toc[i].dwStartSector);
      if ( toc [i].bTrack != i+1 )
	  toc [i].bTrack = i+1;
  }
  ReadTocTextSCSI();
  return --tracks;           /* without lead-out */
}

/* a contribution from Boris for IMS cdd 522 */

/* read the table of contents from the cd and fill the TOC array */
unsigned ReadTocCdrSCSI ( TOC *toc )
{
  unsigned i;
  unsigned tracks;

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)bufferTOC;
        scmd.size = CD_FRAMESIZE;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0xE5;
        scmd.cdb.g1_cdb.lun = global.lun;
        g1_cdbaddr(&scmd.cdb.g1_cdb, 1);
        g1_cdblen(&scmd.cdb.g1_cdb, CD_FRAMESIZE);

        if (jes_verbose) fprintf(stderr, "\nRead TOC (philips)...");
  /* do the scsi cmd (read table of contents) */
  if (scsicmd ( "read track info philips") < 0)
      FatalError ("Read TrackInfo failed.\n");

  tracks = bufferTOC[1];
  if (tracks > MAXTRK) return 0;
  if (tracks == 0) return 0;

  /* copy to our structure and convert start sector */
  for (i = 0; i < tracks; i++)
    {
        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)bufferTOC;
        scmd.size = CD_FRAMESIZE;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0xE5;
        scmd.cdb.g1_cdb.lun = global.lun;
        g1_cdbaddr(&scmd.cdb.g1_cdb, i + 1);
        g1_cdblen(&scmd.cdb.g1_cdb, CD_FRAMESIZE);

      if (scsicmd ( "read track info philips") < 0)
          FatalError ("Read TrackInfo failed.\n");

      toc[i].reserved1 = 0;
      toc[i].bFlags = bufferTOC[10];
      toc[i].bTrack = i + 1;
      toc[i].reserved2 = 0;
      toc[i].dwStartSector = adjust_ssize * be32_to_cpu(GET_UINT_FROM_CHARP(bufferTOC+2));
      toc[i].ISRC[0] = 0;
      if ( toc [i].bTrack != i+1 )
	  toc [i].bTrack = i+1;
    }

  toc[i].reserved1 = 0;
  toc[i].bFlags = 0;
  toc[i].bTrack = i + 1;
  toc[i].reserved2 = 0;
  toc[i].dwStartSector = adjust_ssize * (be32_to_cpu(GET_UINT_FROM_CHARP(bufferTOC + 2)) +
                                        be32_to_cpu(GET_UINT_FROM_CHARP(bufferTOC + 6)));
  toc[i].ISRC[0] = 0;

  ReadTocTextSCSI();
  return tracks;
}

/* Read max. SectorBurst of cdda sectors to bufferCdda
   via standard SCSI-2 Read(10) command */
void ReadStandard (unsigned char *p, long lSector, unsigned long SectorBurstVal )
{
  /* READ10, flags, block1 msb, block2, block3, block4 lsb, reserved, 
     transfer len msb, transfer len lsb, block addressing mode */
  int count = 0;

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)p;
        scmd.size = SectorBurstVal*CD_FRAMESIZE_RAWER;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0x28;
        scmd.cdb.g1_cdb.lun = global.lun;
        g1_cdbaddr(&scmd.cdb.g1_cdb, lSector);
        g1_cdblen(&scmd.cdb.g1_cdb, SectorBurstVal);
        if (jes_verbose) fprintf(stderr, "\nReadStandard10 CDDA...");
  while (scsicmd("ReadStandard10") < 0
	 && count--)
    ;
  if (count < 0)
    FatalError ("Read CD-ROM10 failed\n");
}


/* Read max. SectorBurst of cdda sectors to bufferCdda
   via vendor-specific ReadCdda(10) command */
void ReadCdda10 (unsigned char *p, long lSector, unsigned long SectorBurstVal )
{
  /* READ10, flags, block1 msb, block2, block3, block4 lsb, reserved, 
     transfer len msb, transfer len lsb, block addressing mode */
  int count = 0;

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)p;
        scmd.size = SectorBurstVal*CD_FRAMESIZE_RAWER;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G1_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g1_cdb.cmd = 0xd4;
        scmd.cdb.g1_cdb.lun = global.lun;
        g1_cdbaddr(&scmd.cdb.g1_cdb, lSector);
        g1_cdblen(&scmd.cdb.g1_cdb, SectorBurstVal);
        if (jes_verbose) fprintf(stderr, "\nReadNEC10 CDDA...");
  while (scsicmd("Read10 NEC") < 0
	 && count--)
    ;
  if (count < 0)
    FatalError ("Read CD-ROM10 (NEC) failed\n");
}


/* Read max. SectorBurst of cdda sectors to bufferCdda
   via vendor-specific ReadCdda(12) command */
void ReadCdda12 (unsigned char *p, long lSector, unsigned long SectorBurstVal )
{
  int count = 0;

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)p;
        scmd.size = SectorBurstVal*CD_FRAMESIZE_RAWER;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G5_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g5_cdb.cmd = 0xD8;
        scmd.cdb.g5_cdb.lun = global.lun;
        g5_cdbaddr(&scmd.cdb.g5_cdb, lSector);
        g5_cdblen(&scmd.cdb.g5_cdb, SectorBurstVal);
        scmd.cdb.g5_cdb.res10 = 0x02;	/* CDDA plus subcodes */

        if (jes_verbose) fprintf(stderr, "\nReadSony12 CDDA...");
  while (scsicmd("Read12") < 0
	 && count--)
    ;
  if (count < 0)
    FatalError ("Read CD-ROM12 failed\n");
}

/* Read max. SectorBurst of cdda sectors to bufferCdda
   via MMC standard READ CD command */
void ReadCddaMMC12 (unsigned char *p, long lSector, unsigned long SectorBurstVal )
{
  int count = 0;

        fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
        scmd.addr = (caddr_t)p;
        scmd.size = SectorBurstVal*CD_FRAMESIZE_RAWER;
        scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
        scmd.cdb_len = SC_G5_CDBLEN;
        scmd.sense_len = CCS_SENSE_LEN;
        scmd.target = global.target;
        scmd.cdb.g5_cdb.cmd = 0xBE;
        scmd.cdb.g5_cdb.lun = global.lun;
        scmd.cdb.g5_cdb.res = 1 << 1; /* expected sector type field CDDA */
        scmd.cdb.g5_cdb.res = 0; /* expected sector type field ALL */
        scmd.cdb.g5_cdb.res10 = 1 << 2; /* plus all subchannels */
        scmd.cdb.g5_cdb.res10 = 1 ; /* plus all subchannels RAW */
        g5_cdbaddr(&scmd.cdb.g5_cdb, lSector);
        g5x_cdblen(&scmd.cdb.g5_cdb, SectorBurstVal);
	scmd.cdb.g5_cdb.count[3] = 1 << 4 | 3 << 5 ;	/* User data  + all headers */

        if (jes_verbose) fprintf(stderr, "\nReadMMC12 CDDA...");
  while (scsicmd("ReadCD MMC 12") < 0
	 && count--)
    ;
  if (count < 0)
    FatalError ("ReadCD MMC 12 failed\n");
}


/********* non standardized speed selects ***********************/

void SpeedSelectSCSIToshiba (unsigned speed)
{
  static unsigned char mode [4 + 3];
  unsigned char *page = mode + 4;
  int retval;

  if (speed != 1 && speed != 2) {
    fprintf(stderr, "invalid speed setting %d for Toshiba\n", speed);
    return;
  }

  fillbytes((caddr_t)mode, sizeof(mode), '\0');
  /* the first 4 mode bytes are zero. */
  page[0] = 0x20;
  page[1] = 1;
  page[2] = speed == 1 ? 0 : 1;   /* 0 for single speed */
  fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');

  if (jes_verbose) fprintf(stderr, "\nspeed select Toshiba...");
  /* do the scsi cmd */
  if ((retval = mode_select(mode, 7, 0, 1)) < 0)
        perror ("speed select Toshiba failed\n");
}

void SpeedSelectSCSINEC (unsigned speed)
{
  static unsigned char mode [4 + 8];
  unsigned char *page = mode + 4;
  int retval;

  if (speed != 1 && speed != 2) {
    fprintf(stderr, "invalid speed setting %d for NEC\n", speed);
    return;
  }

  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.target = global.target;
  scmd.cdb.g1_cdb.cmd = 0xC5;
  scmd.cdb.g1_cdb.lun = global.lun;
  scmd.cdb.g1_cdb.addr[0] = 0 ? 1 : 0 | 1 ? 0x10 : 0;
  g1_cdblen(&scmd.cdb.g1_cdb, 12);

  if (jes_verbose) fprintf(stderr, "\nspeed select NEC...");
  /* do the scsi cmd */
  if ((retval = scsicmd("speed select NEC")) < 0)
        perror ("speed select NEC failed\n");
}

void SpeedSelectSCSIPhilipsCDD2600 (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;

  if (speed != 1 && speed != 2 && speed != 4) {
    fprintf(stderr, "invalid speed setting %d for Philips CDD2600\n", speed);
    return;
  }

  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 (jes_verbose) fprintf(stderr, "\nspeed select Philips...");
  /* do the scsi cmd */
  if ((retval = mode_select(mode, 12, 0, 1)) < 0)
        perror ("speed select PhilipsCDD2600 failed\n");
}

void SpeedSelectSCSISony (unsigned speed)
{
  static unsigned char mode [4 + 4];
  unsigned char *page = mode + 4;
  int retval;

  /* speed values > 1 are drive dependent */
  if (speed > 4) speed = 8;

  fillbytes((caddr_t)mode, sizeof(mode), '\0');
  /* the first 4 mode bytes are zero. */
  page[0] = 0x31;
  page[1] = 2;
  page[2] = speed / 2;

  if (jes_verbose) fprintf(stderr, "\nspeed select Sony...");
  /* do the scsi cmd */
  if ((retval = mode_select(mode, 8, 0, 1)) < 0)
        perror ("speed select Sony failed\n");
}

void SpeedSelectSCSIYamaha (unsigned speed)
{
  static unsigned char mode [4 + 4];
  unsigned char *page = mode + 4;
  int retval;

  /* speed values > 1 are drive dependent */
  if (speed > 4) speed = 8;

  fillbytes((caddr_t)mode, sizeof(mode), '\0');
  /* the first 4 mode bytes are zero. */
  page[0] = 0x31;
  page[1] = 2;
  page[2] = (speed / 2) << 4;

  if (jes_verbose) fprintf(stderr, "\nspeed select Yamaha...");
  /* do the scsi cmd */
  if ((retval = mode_select(mode, 8, 0, 1)) < 0)
        perror ("speed select Yamaha failed\n");
}

void SpeedSelectSCSIMMC (unsigned speed)
{
  int spd;

   if (speed == 0) {
      spd = 0xFFFF;
   } else {
      spd = 176 * speed;
   }
        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.target = global.target;
        scmd.cdb.g5_cdb.cmd = 0xBB;
        scmd.cdb.g5_cdb.lun = global.lun;
        i_to_short(&scmd.cdb.g5_cdb.addr[0], spd);
        i_to_short(&scmd.cdb.g5_cdb.addr[2], spd);

        if (jes_verbose) fprintf(stderr, "\nspeed select MMC...");
        if (scsicmd("set cd speed") < 0)
                perror ("speed select MMC failed\n");
}

/* request vendor brand and model */
unsigned char *Inquiry ( void )
{
  static unsigned char *Inqbuffer = NULL;
   
  if (Inqbuffer == NULL) {
    Inqbuffer = malloc(42);
    if (Inqbuffer == NULL) {
      fprintf(stderr, "Cannot allocate memory for inquiry command in line %d\n", __LINE__);
        return NULL;
    }
  }

  fillbytes(Inqbuffer, 42, '\0');
  fillbytes((caddr_t)&scmd, sizeof(scmd), '\0');
  scmd.addr = (caddr_t)Inqbuffer;
  scmd.size = 42;
  scmd.flags = SCG_RECV_DATA|SCG_DISRE_ENA;
  scmd.cdb_len = SC_G0_CDBLEN;
  scmd.sense_len = CCS_SENSE_LEN;
  scmd.target = global.target;
  scmd.cdb.g0_cdb.cmd = SC_INQUIRY;
  scmd.cdb.g0_cdb.lun = global.lun;
  scmd.cdb.g0_cdb.count = 42;
        
  if (scsicmd("inquiry") < 0)
     return (NULL);

  if (jes_verbose)
     scsiprbytes("Inquiry Data   :", (u_char *)Inqbuffer, 22 - scmd.resid);

  return (Inqbuffer);
}

#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 ( void )
{

  /* 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.target = global.target;
  scmd.cdb.g0_cdb.cmd = SC_TEST_UNIT_READY;
  scmd.cdb.g0_cdb.lun = global.lun;
        
  if (jes_verbose) fprintf(stderr, "\ntest unit ready...");
  jes_silent++;
  if (0 < scsicmd("test unit ready")) {
    jes_silent--;
    return 1;
  }
  jes_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 Play_atSCSI ( unsigned int from_sector, unsigned int sectors)
{
  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.target = global.target;
  scmd.cdb.g1_cdb.cmd = 0x47;
  scmd.cdb.g1_cdb.lun = global.lun;
  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 (jes_verbose) fprintf(stderr, "\nplay sectors...");
  /* do the scsi cmd */
  return scsicmd("play sectors") > 0 ? -1 : 0;
}
