summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Shalayeff <mickey@cvs.openbsd.org>1996-05-16 09:28:57 +0000
committerMichael Shalayeff <mickey@cvs.openbsd.org>1996-05-16 09:28:57 +0000
commitcce64b8c239c48d8a40a028c26cb08ad645d3840 (patch)
tree17a86a55d7a69b5223b445894ab0899f54cb7ef2
parentcf3fdeb83b3dc4d2a89ee0ac772ea3eb608e3c33 (diff)
from NetBSD PR#812:
allow CDDA disks to be read. not tested, anyone w/ SCSI CD is ought to. here is the test program (not tested too ;): #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 <scsi/scsi_all.h> #include <scsi/scsi_cd.h> #include <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); }
-rw-r--r--sys/scsi/cd.c100
-rw-r--r--sys/scsi/scsi_cd.h17
-rw-r--r--sys/sys/cdio.h3
3 files changed, 119 insertions, 1 deletions
diff --git a/sys/scsi/cd.c b/sys/scsi/cd.c
index 08ee3872ae7..31105662554 100644
--- a/sys/scsi/cd.c
+++ b/sys/scsi/cd.c
@@ -86,11 +86,17 @@ struct cd_softc {
#define CDF_WLABEL 0x04 /* label is writable */
#define CDF_LABELLING 0x08 /* writing label */
#define CDF_ANCIENT 0x10 /* disk is ancient; for minphys */
+#ifdef CDDA
+#define CDF_CDDAMODE 0x08
+#endif
struct scsi_link *sc_link; /* contains our targ, lun, etc. */
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;
};
@@ -267,6 +273,18 @@ cdopen(dev, flag, fmt, p)
if ((error = cdlock(cd)) != 0)
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,
@@ -276,6 +294,16 @@ cdopen(dev, flag, fmt, p)
error = EIO;
goto bad3;
}
+#ifdef CDDA
+ /*
+ * If it's in CDDA mode, process may only open the
+ * raw partition
+ */
+ if (cd->flags & CDF_CDDAMODE && part != RAW_PART) {
+ error = EBUSY;
+ goto bad3;
+ }
+#endif
} else {
/* Check that it is still responding and ok. */
error = scsi_test_unit_ready(sc_link,
@@ -432,6 +460,18 @@ cdstrategy(bp)
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
/*
* Do bounds checking, adjust transfer. if error, process.
* If end of partition, just return.
@@ -909,6 +949,66 @@ cdioctl(dev, cmd, addr, flag, p)
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)
diff --git a/sys/scsi/scsi_cd.h b/sys/scsi/scsi_cd.h
index ddf2431b2da..2135a4a6ce6 100644
--- a/sys/scsi/scsi_cd.h
+++ b/sys/scsi/scsi_cd.h
@@ -191,6 +191,23 @@ union cd_pages {
} 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;
diff --git a/sys/sys/cdio.h b/sys/sys/cdio.h
index 0ef183f8ad5..22707e96d74 100644
--- a/sys/sys/cdio.h
+++ b/sys/sys/cdio.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cdio.h,v 1.4 1996/03/03 12:11:24 niklas Exp $ */
+/* $OpenBSD: cdio.h,v 1.5 1996/05/16 09:28:54 mickey Exp $ */
/* $NetBSD: cdio.h,v 1.11 1996/02/19 18:29:04 scottr Exp $ */
#ifndef _SYS_CDIO_H_
@@ -174,6 +174,7 @@ struct ioc_vol {
#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;