Subject: kern/812: CD code doesn't grok CDDA mode
To: None <gnats-admin@NetBSD.ORG>
From: John Kohl <jtk@kolvir.blrc.ma.us>
List: netbsd-bugs
Date: 02/21/1995 19:50:04
>Number:         812
>Category:       kern
>Synopsis:       CD code doesn't grok CDDA mode
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    kern-bug-people (Kernel Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Tue Feb 21 19:50:03 1995
>Originator:     John Kohl
>Organization:
NetBSD Kernel Hackers `R` Us
>Release:        -current, 21 Feb 1995
>Environment:
	
System: NetBSD kolvir 1.0A NetBSD 1.0A (KOLVIR) #91: Tue Feb 21 19:40:15 EST 1995 jtk@kolvir:/u1/NetBSD-current/src/sys/arch/i386/compile/KOLVIR i386


>Description:
The SCSI CD driver doesn't understand how to read CDDA frames over the
SCSI bus.  Some drives, such as the Toshiba XM3401-TA have a
block/density mode you can select to get such frames.

>How-To-Repeat:
Try to read samples from an audio CD

>Fix:

Here are diffs for using a SCSI CD-ROM to read CD Digital Audio data.

I've enclosed a sample program which reads said data.

The diffs include fixes so that builds with DEBUGSCSI work.

[2 scsicmd.c (text/plain)]

#define CDDA

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/scsiio.h>
#include <sys/cdio.h>
#include </sys/scsi/scsi_all.h>
#include </sys/scsi/scsi_cd.h>
#include </sys/scsi/scsi_disk.h>

extern int errno;

void
usage()
{
    fprintf(stderr, "usage: cdda -d device -b blkcnt -o offset >output\n");
    exit(1);
}

char databuf[CD_DA_BLKSIZ];

main(int argc, char *argv[])
{
    int ch;
    int fd;
    off_t offset = 0;
    int cnt = 0;
    char *dev = 0;
    struct scsi_rw_big read_cmd;
    struct scsi_mode_sense sense_cmd;
    struct cd_mode_data bdesc;
    scsireq_t req;

    while ((ch = getopt(argc, argv, "d:b:o:")) != -1) {
	switch (ch) {
	case 'd':
	    dev = optarg;
	    break;
	case 'b':
	    cnt = atoi(optarg);
	    if (cnt <= 0)
		usage();
	    break;
	case 'o':
	    offset = atoi(optarg);
	    break;
	case '?':
	default:
	    usage();
	}
    }
    if (dev == NULL || cnt == 0)
	usage();
    fd = open(dev, O_RDONLY);
    if (fd == -1)
	err(1,"can't open device %s", dev);
#ifdef DEBUG
    ch = SC_DB_FLOW;
    ioctl(fd, SCIOCDEBUG, &ch);
#endif
    ch = 1;
    if (ioctl(fd, CDIOCSETCDDA, &ch) == -1)
	warn("can't set CDDA mode");

    read_cmd.opcode = READ_BIG;		/* READ10 */
    read_cmd.byte2 = 0;			/* no relative */
    read_cmd.reserved = 0;
    read_cmd.length2 = 0;
    read_cmd.length1 = 1;		/* read one block at a time.
					   hope it caches! */
    read_cmd.control = 0;		/* LBA mode, leave flag & link zero */

    for (; cnt > 0; cnt--, offset++) {
	read_cmd.addr_3 = (offset >> 24) & 0xff;
	read_cmd.addr_2 = (offset >> 16) & 0xff;
	read_cmd.addr_1 = (offset >> 8) & 0xff;
	read_cmd.addr_0 = offset & 0xff;
	memset(&req, 0, sizeof(req));
	req.flags = SCCMD_READ;
	/* timeout is in milliseconds--not that it's obvious from the
           include files!  */
	req.timeout = 10000;		/* 10 sec */

	bcopy(&read_cmd, req.cmd, sizeof(read_cmd));
	req.cmdlen = sizeof(read_cmd);
	req.databuf = databuf;
	req.datalen = sizeof(databuf);
	req.senselen = sizeof(req.sense); /* XXX */
	if (ioctl(fd, SCIOCCOMMAND, &req) == -1) {
	    fprintf(stderr, "bad ioctl: %d\n", errno);
	    ch = 0;
	    ioctl(fd, CDIOCSETCDDA, &ch);
#ifdef DEBUG
	    ioctl(fd, SCIOCDEBUG, &ch);
#endif
	    exit(1);
	}
	if (req.retsts != 0 || req.error != 0) {
	    ch = 0;
	    ioctl(fd, CDIOCSETCDDA, &ch);
#ifdef DEBUG
	    ioctl(fd, SCIOCDEBUG, &ch);
#endif
	    errx(1,"return status %d, error %d\n", req.retsts, req.error);
	}
	if (req.datalen_used != sizeof(databuf)) {
	    ch = 0;
	    ioctl(fd, CDIOCSETCDDA, &ch);
#ifdef DEBUG
	    ioctl(fd, SCIOCDEBUG, &ch);
#endif
	    errx(1,"didn't get full buffer back (%x)", req.datalen_used);
	}
	write(1, databuf, sizeof(databuf));
    }
    ch = 0;
    if (ioctl(fd, CDIOCSETCDDA, &ch) == -1)
	warn("can't reset CDDA mode");
#ifdef DEBUG
    ioctl(fd, SCIOCDEBUG, &ch);
#endif
    close(fd);
    exit(0);
}


[3 diffs (text/plain)]

===================================================================
RCS file: scsi/RCS/cd.c,v
retrieving revision 1.1
diff -ubw -r1.1 scsi/cd.c
--- 1.1	1994/12/13 03:44:04
+++ scsi/cd.c	1994/12/15 04:46:08
@@ -90,16 +90,23 @@
 #define	CDF_LOCKED	0x01
 #define	CDF_WANTED	0x02
 #define	CDF_BSDLABEL	0x04
+#ifdef CDDA
+#define	CDF_CDDAMODE	0x08
+#endif
 	struct scsi_link *sc_link;	/* address of scsi low level switch */
 	struct cd_parms {
 		int blksize;
 		u_long disksize;	/* total number sectors */
 	} params;
+#ifdef CDDA
+	struct cd_parms orig_params;	/* filled in when CD-DA mode starts */
+#endif
 	struct buf buf_queue;
 };
 
 int cdmatch __P((struct device *, void *, void *));
 void cdattach __P((struct device *, struct device *, void *));
+int  cd_set_mode __P((struct cd_softc *, struct cd_mode_data *));
 
 struct cfdriver cdcd = {
 	NULL, "cd", cdmatch, cdattach, DV_DISK, sizeof(struct cd_softc)
@@ -215,6 +222,18 @@
 			return error;
 	}
 
+#ifdef CDDA
+	/*
+	 * We can't open the block device if the drive is in CDDA
+	 * mode.  If we allow such opens, either the drive will get
+	 * quite unhappy about undersized I/O or the BSD I/O subsystem
+	 * will start trashing memory with disk transfers overrunning
+	 * the end of their buffers.  Either way, it's Bad.
+	 */
+	if (cd->flags & CDF_CDDAMODE && fmt == S_IFBLK)
+	    return EBUSY;
+#endif
+
 	if (cd->sc_dk.dk_openmask != 0) {
 		/*
 		 * If any partition is open, but the disk has been invalidated,
@@ -222,6 +241,14 @@
 		 */
 		if ((sc_link->flags & SDEV_MEDIA_LOADED) == 0)
 			return ENXIO;
+#ifdef CDDA
+		/*
+		 * If it's in CDDA mode, process may only open the
+		 * raw partition
+		 */
+		if (cd->flags & CDF_CDDAMODE && part != RAW_PART)
+		    return EBUSY;
+#endif
 	} else {
 		cd->flags |= CDF_LOCKED;
 
@@ -412,6 +439,18 @@
 	 */
 	if (bp->b_bcount == 0)
 		goto done;
+#ifdef CDDA
+	/*
+	 * If in CDDA mode, return immediately (the user must issue
+	 * SCSI read commands directly using SCIOCCOMMAND).  The BSD
+	 * I/O subsystem just can't deal with bizzare block sizes like
+	 * those used for CD-DA frames.
+	 */
+	if (cd->flags & CDF_CDDAMODE) {
+	    bp->b_error = EIO;
+	    goto bad;
+	}
+#endif
 	/*
 	 * Decide which unit and partition we are talking about
 	 */
@@ -807,6 +846,64 @@
 		return 0;
 	case CDIOCRESET:
 		return cd_reset(cd);
+#ifdef CDDA
+	case CDIOCSETCDDA: {
+	    int onoff = *(int *)addr;
+	    /* select CD-DA mode */
+	    struct cd_mode_data data;
+
+	    if (CDPART(dev) != RAW_PART)
+		return ENOTTY;
+	    if (error = cd_get_mode(cd, &data, AUDIO_PAGE))
+		return error;
+	    if (onoff) {
+		/* turn it on */
+		if (cd->flags & CDF_CDDAMODE)
+		    return EALREADY;
+		/*
+		 * do not permit changing of block size if the block device
+		 * is open, or if some other partition (not a raw partition)
+		 * is open.
+		 */
+		if (cd->sc_dk.dk_bopenmask ||
+		    (cd->sc_dk.dk_copenmask & ~(1 << RAW_PART)))
+		    return EBUSY;
+
+		data.blk_desc.density = CD_DA_DENSITY_CODE;
+		data.blk_desc.blklen[0] = (CD_DA_BLKSIZ >> 16) & 0xff;
+		data.blk_desc.blklen[1] = (CD_DA_BLKSIZ >> 8) & 0xff;
+		data.blk_desc.blklen[2] = CD_DA_BLKSIZ & 0xff;
+		if (cd_set_mode(cd, &data) != 0) {
+		    return EIO;
+		}
+		cd->orig_params = cd->params;
+		cd->flags |= CDF_CDDAMODE;
+	    } else {
+		if (!(cd->flags & CDF_CDDAMODE))
+		    return EALREADY;
+		/* turn it off */
+		data.blk_desc.density = CD_NORMAL_DENSITY_CODE;
+		data.blk_desc.blklen[0] = (cd->orig_params.blksize >> 16) & 0xff;
+		data.blk_desc.blklen[1] = (cd->orig_params.blksize >> 8) & 0xff;
+		data.blk_desc.blklen[2] = (cd->orig_params.blksize) & 0xff;
+		if (cd_set_mode(cd, &data) != 0) {
+		    return EIO;
+		}
+		cd->flags &= ~CDF_CDDAMODE;
+		cd->params = cd->orig_params;
+	    }
+	    if (cd_get_parms(cd, 0) != 0) {
+		data.blk_desc.density = CD_NORMAL_DENSITY_CODE;
+		data.blk_desc.blklen[0] = (cd->orig_params.blksize >> 16) & 0xff;
+		data.blk_desc.blklen[1] = (cd->orig_params.blksize >> 8) & 0xff;
+		data.blk_desc.blklen[2] = (cd->orig_params.blksize) & 0xff;
+		(void) cd_set_mode(cd, &data); /* it better work...! */
+		cd->params = cd->orig_params;
+		return EIO;
+	    }
+	    return 0;
+	}
+#endif
 	default:
 		if (CDPART(dev) != RAW_PART)
 			return ENOTTY;
===================================================================
RCS file: scsi/RCS/scsi_base.c,v
retrieving revision 1.1
diff -ubw -r1.1 scsi/scsi_base.c
--- 1.1	1994/12/15 00:08:11
+++ scsi/scsi_base.c	1994/12/15 00:07:23
@@ -471,7 +471,7 @@
 		if (xs->bp)
 			return EJUSTRETURN;
 	doit:
-		SC_DEBUG(sc_link, SDEV_DB3, ("back in cmd()\n"));
+		SC_DEBUG(xs->sc_link, SDEV_DB3, ("back in cmd()\n"));
 		if ((error = sc_err1(xs, 0)) != ERESTART)
 			return error;
 		goto retry;
@@ -882,9 +882,9 @@
 void
 show_mem(address, num)
 	unsigned char *address;
-	u_int32 num;
+	u_int32_t num;
 {
-	u_int32 x, y;
+	u_int32_t x, y;
 	printf("------------------------------");
 	for (y = 0; y < num; y += 1) {
 		if (!(y % 16))
===================================================================
RCS file: scsi/RCS/scsi_cd.h,v
retrieving revision 1.1
diff -ubw -r1.1 scsi/scsi_cd.h
--- 1.1	1994/12/13 03:46:07
+++ scsi/scsi_cd.h	1994/12/15 04:11:56
@@ -203,6 +203,23 @@
 	} audio;
 };
 
+#ifdef CDDA
+/*
+ * There are 2352 bytes in a CD digital audio frame.  One frame is 1/75 of a
+ * second, at 44.1kHz sample rate, 16 bits/sample, 2 channels.
+ *
+ * The frame data have the two channels interleaved, with the left
+ * channel first.  Samples are little endian 16-bit signed values.
+ */
+#define CD_DA_BLKSIZ		2352	/* # bytes in CD-DA frame */
+#ifndef CD_NORMAL_DENSITY_CODE
+#define	CD_NORMAL_DENSITY_CODE	0x00	/* from Toshiba CD-ROM specs */
+#endif
+#ifndef CD_DA_DENSITY_CODE
+#define	CD_DA_DENSITY_CODE	0x82	/* from Toshiba CD-ROM specs */
+#endif
+#endif /* CDDA */
+
 struct cd_mode_data {
 	struct scsi_mode_header header;
 	struct scsi_blk_desc blk_desc;
===================================================================
RCS file: scsi/RCS/scsi_debug.h,v
retrieving revision 1.1
diff -ubw -r1.1 scsi/scsi_debug.h
--- 1.1	1994/12/15 00:10:45
+++ scsi/scsi_debug.h	1994/12/15 00:10:54
@@ -18,7 +18,7 @@
 #define	SDEV_DB4		0x80	/* level 4 debugging for this dev */
 
 /* target and LUN we want to debug */
-#define	DEBUGTARG 9 /*9 = dissable*/
+#define	DEBUGTARGET 9 /*9 = disable*/
 #define	DEBUGLUN  0
 #define	DEBUGLEVEL  	(SDEV_DB1|SDEV_DB2)
  
===================================================================
RCS file: scsi/RCS/scsiconf.h,v
retrieving revision 1.1
diff -ubw -r1.1 scsi/scsiconf.h
--- 1.1	1994/12/15 00:07:32
+++ scsi/scsiconf.h	1994/12/15 00:07:39
@@ -269,7 +269,7 @@
 
 void show_scsi_xs __P((struct scsi_xfer *));
 void show_scsi_cmd __P((struct scsi_xfer *));
-void show_mem __P((unsigned char *, int));
+void show_mem __P((unsigned char *, u_int32_t));
 
 void lto3b __P((u_int32_t val, u_int8_t *bytes));
 u_int32_t _3btol __P((u_int8_t *bytes));
===================================================================
RCS file: sys/RCS/cdio.h,v
retrieving revision 1.1
diff -ubw -r1.1 sys/cdio.h
--- 1.1	1994/12/15 00:46:54
+++ sys/cdio.h	1994/12/15 04:18:50
@@ -138,6 +138,7 @@
 #define	CDIOCEJECT	_IO('c', 24)
 #define	CDIOCALLOW	_IO('c', 25)
 #define	CDIOCPREVENT	_IO('c', 26)
+#define	CDIOCSETCDDA	_IOW('c', 27, int) /* (re)set CDDA reading mode */
 
 struct ioc_play_msf {
 	u_char	start_m;


>Audit-Trail:
>Unformatted: