summaryrefslogtreecommitdiff
path: root/scsi/rz_audio.c
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /scsi/rz_audio.c
Initial source
Diffstat (limited to 'scsi/rz_audio.c')
-rw-r--r--scsi/rz_audio.c1901
1 files changed, 1901 insertions, 0 deletions
diff --git a/scsi/rz_audio.c b/scsi/rz_audio.c
new file mode 100644
index 0000000..4d60fa1
--- /dev/null
+++ b/scsi/rz_audio.c
@@ -0,0 +1,1901 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: rz_audio.c
+ * Author: Alessandro Forin, Carnegie Mellon University
+ * Date: 3/93
+ *
+ * Top layer of the SCSI driver: interface with the MI.
+ * This file contains operations specific to audio CD-ROM devices.
+ * Unlike many others, it sits on top of the rz.c module.
+ */
+
+#include <mach/std_types.h>
+#include <kern/strings.h>
+#include <machine/machspl.h> /* spl definitions */
+#include <vm/vm_kern.h>
+#include <device/ds_routines.h>
+
+#include <scsi/compat_30.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi2.h>
+#include <scsi/scsi_defs.h>
+#include <scsi/rz.h>
+
+#if (NSCSI > 0)
+
+#define private static
+
+/* some data is two BCD digits in one byte */
+#define bcd_to_decimal(b) (((b)&0xf) + 10 * (((b) >> 4) & 0xf))
+#define decimal_to_bcd(b) ((((b) / 10) << 4) | ((b) % 10))
+
+/*
+ * Regular use of a CD-ROM is for data, and is handled
+ * by the default set of operations. Ours is for funtime..
+ */
+
+extern char *sccdrom_name();
+int cd_strategy();
+void cd_start();
+
+private scsi_devsw_t scsi_audio = {
+ sccdrom_name, 0, 0, 0, cd_strategy, cd_start, 0, 0
+};
+
+private char unsupported[] = "Device does not support it.";
+
+/*
+ * Unfortunately, none of the vendors appear to
+ * abide by the SCSI-2 standard and many of them
+ * violate or stretch even the SCSI-1 one.
+ * Therefore, we keep a red-list here of the worse
+ * offendors and how to deal with them.
+ * The user is notified of the problem and invited
+ * to solicit his vendor to upgrade the firmware.
+ * [They had plenty of time to do so]
+ */
+typedef struct red_list {
+ char *vendor;
+ char *product;
+ char *rev;
+ /*
+ * The standard MANDATES [par 13.1.6] the play_audio command
+ * at least as a way to discover if the device
+ * supports audio operations at all. This is the only way
+ * we need to use it.
+ */
+ scsi_ret_t (*can_play_audio)( target_info_t *, char *, io_req_t);
+ /*
+ * The standard defines the use of start_stop_unit to
+ * cause the drive to eject the disk.
+ */
+ scsi_ret_t (*eject)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines read_subchannel as a way to
+ * get the current playing position.
+ */
+ scsi_ret_t (*current_position)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines read_table_of_content to get
+ * the listing of audio tracks available.
+ */
+ scsi_ret_t (*read_toc)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines read_subchannel as the way to
+ * report the current audio status (playing/stopped/...).
+ */
+ scsi_ret_t (*get_status)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines two ways to issue a play command,
+ * depending on the type of addressing used.
+ */
+ scsi_ret_t (*play_msf)( target_info_t *, char *, io_req_t );
+ scsi_ret_t (*play_ti)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines the pause_resume command to
+ * suspend or resume playback of audio data.
+ */
+ scsi_ret_t (*pause_resume)( target_info_t *, char *, io_req_t );
+ /*
+ * The standard defines the audio page among the
+ * mode selection options as a way to control
+ * both volume and connectivity of the channels
+ */
+ scsi_ret_t (*volume_control)( target_info_t *, char *, io_req_t );
+} red_list_t;
+
+#define if_it_can_do(some_cmd) \
+ if (tgt->dev_info.cdrom.violates_standards && \
+ tgt->dev_info.cdrom.violates_standards->some_cmd) \
+ rc = (*tgt->dev_info.cdrom.violates_standards->some_cmd) \
+ (tgt,cmd,ior); \
+ else
+
+/*
+ * So now that you know what they should have implemented :-),
+ * check at the end of the file what the naughty boys did instead.
+ */
+/* private red_list_t audio_replacements[]; / * at end */
+
+/*
+ * Forward decls
+ */
+private void decode_status( char *buf, unsigned char audio_status );
+void zero_ior( io_req_t );
+
+/*
+ * Open routine. Does some checking, sets up
+ * the replacement pointer.
+ */
+io_return_t
+cd_open(
+ int dev,
+ dev_mode_t mode,
+ io_req_t req)
+{
+ scsi_softc_t *sc = 0;
+ target_info_t *tgt;
+ int ret;
+ scsi_ret_t rc;
+ io_req_t ior = 0;
+ vm_offset_t mem = 0;
+ extern boolean_t rz_check();
+
+ if (!rz_check(dev, &sc, &tgt)) {
+ /*
+ * Probe it again: might have installed a new device
+ */
+ if (!sc || !scsi_probe(sc, &tgt, rzslave(dev), ior))
+ return D_NO_SUCH_DEVICE;
+ bzero(&tgt->dev_info, sizeof(tgt->dev_info));
+ }
+
+ /*
+ * Check this is indeded a CD-ROM
+ */
+ if (tgt->dev_ops != &scsi_devsw[SCSI_CDROM]) {
+ rz_close(dev);
+ return D_NO_SUCH_DEVICE;
+ }
+
+ /*
+ * Switch to audio ops, unless some wrong
+ */
+ tgt->dev_ops = &scsi_audio;
+
+ /*
+ * Bring unit online
+ */
+ ret = rz_open(dev, mode, req);
+ if (ret) goto bad;
+
+ /* Pessimistic */
+ ret = D_INVALID_OPERATION;
+
+ /*
+ * Check if this device is on the red list
+ */
+ {
+ scsi2_inquiry_data_t *inq;
+ private void check_red_list();
+
+ scsi_inquiry(tgt, SCSI_INQ_STD_DATA);
+ inq = (scsi2_inquiry_data_t*)tgt->cmd_ptr;
+
+ check_red_list( tgt, inq );
+
+ }
+
+ /*
+ * Allocate dynamic data
+ */
+ if (kmem_alloc(kernel_map, &mem, PAGE_SIZE) != KERN_SUCCESS)
+ return D_NO_MEMORY;
+ tgt->dev_info.cdrom.result = (void *)mem;
+ tgt->dev_info.cdrom.result_available = FALSE;
+
+ /*
+ * See if this CDROM can play audio data
+ */
+ io_req_alloc(ior,0);
+ zero_ior( ior );
+
+ {
+ char *cmd = 0;
+ if_it_can_do(can_play_audio)
+ rc = scsi_play_audio( tgt, 0, 0, FALSE, ior);
+ }
+
+ if (rc != SCSI_RET_SUCCESS) goto bad;
+
+ io_req_free(ior);
+ return D_SUCCESS;
+
+bad:
+ if (ior) io_req_free(ior);
+ if (mem) kmem_free(kernel_map, mem, PAGE_SIZE);
+ tgt->dev_ops = &scsi_devsw[SCSI_CDROM];
+ return ret;
+}
+
+/*
+ * Close routine.
+ */
+io_return_t
+cd_close(
+ int dev)
+{
+ scsi_softc_t *sc;
+ target_info_t *tgt;
+ vm_offset_t mem;
+
+ if (!rz_check(dev, &sc, &tgt))
+ return D_NO_SUCH_DEVICE;
+ if (!tgt || (tgt->dev_ops != &scsi_audio))
+ return D_NO_SUCH_DEVICE;
+
+ /*
+ * Cleanup state
+ */
+ mem = (vm_offset_t) tgt->dev_info.cdrom.result;
+ tgt->dev_info.cdrom.result = (void *)0;
+ tgt->dev_info.cdrom.result_available = FALSE;
+
+ (void) kmem_free(kernel_map, mem, PAGE_SIZE);
+
+ (void) rz_close(dev);
+
+ tgt->dev_ops = &scsi_devsw[SCSI_CDROM];
+ return D_SUCCESS;
+}
+
+/*
+ * Write routine. It is passed an ASCII string
+ * with the command to be executed.
+ */
+io_return_t
+cd_write(
+ int dev,
+ io_req_t ior)
+{
+ register kern_return_t rc;
+ boolean_t wait = FALSE;
+ io_return_t ret;
+ int count;
+ register char *data;
+ vm_offset_t addr;
+
+ data = ior->io_data;
+ count = ior->io_count;
+ if (count == 0)
+ return D_SUCCESS;
+
+ if (!(ior->io_op & IO_INBAND)) {
+ /*
+ * Copy out-of-line data into kernel address space.
+ * Since data is copied as page list, it will be
+ * accessible.
+ */
+ vm_map_copy_t copy = (vm_map_copy_t) data;
+ kern_return_t kr;
+
+ kr = vm_map_copyout(device_io_map, &addr, copy);
+ if (kr != KERN_SUCCESS)
+ return kr;
+ data = (char *) addr;
+ }
+
+ if (scsi_debug) printf("Got command '%s'\n", data);
+
+ ret = cd_command( dev, data, count, ior);
+
+ if (!(ior->io_op & IO_INBAND))
+ (void) vm_deallocate(device_io_map, addr, ior->io_count);
+ return D_SUCCESS;
+}
+
+/*
+ * Read routine. Returns an ASCII string with the results
+ * of the last command executed.
+ */
+io_return_t
+cd_read(
+ int dev,
+ io_req_t ior)
+{
+ target_info_t *tgt;
+ kern_return_t rc;
+ natural_t count;
+
+ /*
+ * Allocate memory for read buffer.
+ */
+ count = (natural_t)ior->io_count;
+ if (count > PAGE_SIZE)
+ return D_INVALID_SIZE; /* sanity */
+
+ rc = device_read_alloc(ior, count);
+ if (rc != KERN_SUCCESS)
+ return rc;
+
+ if (scsi_debug) printf("Got read req for %d bytes\n", count);
+
+ /*
+ * See if last cmd left some to say
+ */
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+ if (tgt->dev_info.cdrom.result_available) {
+ int len;
+
+ tgt->dev_info.cdrom.result_available = FALSE;
+ len = strlen(tgt->dev_info.cdrom.result)+1;
+
+ if (count > len)
+ count = len;
+ bcopy(tgt->dev_info.cdrom.result, ior->io_data, count);
+
+ } else {
+# define noway "No results pending"
+ count = (count > sizeof(noway)) ? sizeof(noway) : count;
+ bcopy(noway, ior->io_data, count);
+ }
+
+ ior->io_residual = ior->io_count - count;
+ return D_SUCCESS;
+}
+
+/*
+ * This does all the work
+ */
+io_return_t
+cd_command(
+ int dev,
+ char *cmd,
+ int count,
+ io_req_t req)
+{
+ target_info_t *tgt;
+ io_req_t ior;
+ io_return_t ret = D_INVALID_OPERATION;
+ scsi_ret_t rc;
+ char *buf;
+
+ tgt = scsi_softc[rzcontroller(dev)]->target[rzslave(dev)];
+
+ buf = tgt->dev_info.cdrom.result;
+ tgt->dev_info.cdrom.result_available = FALSE;
+
+ io_req_alloc(ior,0);
+ zero_ior( ior );
+
+ switch (cmd[0]) {
+
+ case 'E':
+ /* "Eject" */
+ /* too many dont support it. Sigh */
+ tgt->flags |= TGT_OPTIONAL_CMD;
+ (void) scsi_medium_removal( tgt, TRUE, ior);
+ tgt->flags &= ~TGT_OPTIONAL_CMD;
+
+ zero_ior( ior );
+
+ if_it_can_do(eject)
+ rc = scsi_start_unit(tgt, SCSI_CMD_SS_EJECT, ior);
+ break;
+
+ case 'G':
+ switch (cmd[4]) {
+
+ case 'P':
+ /* "Get Position MSF|ABS" */
+ if_it_can_do(current_position) {
+ rc = scsi_read_subchannel(tgt,
+ cmd[13] == 'M',
+ SCSI_CMD_RS_FMT_CURPOS,
+ 0,
+ ior);
+ if (rc == SCSI_RET_SUCCESS) {
+ cdrom_chan_curpos_t *st;
+ st = (cdrom_chan_curpos_t *)tgt->cmd_ptr;
+ if (cmd[13] == 'M')
+ sprintf(buf, "MSF Position %d %d %d %d %d %d",
+ (integer_t)st->subQ.absolute_address.msf.minute,
+ (integer_t)st->subQ.absolute_address.msf.second,
+ (integer_t)st->subQ.absolute_address.msf.frame,
+ (integer_t)st->subQ.relative_address.msf.minute,
+ (integer_t)st->subQ.relative_address.msf.second,
+ (integer_t)st->subQ.relative_address.msf.frame);
+ else
+ sprintf(buf, "ABS Position %d %d", (integer_t)
+ (st->subQ.absolute_address.lba.lba1<<24)+
+ (st->subQ.absolute_address.lba.lba2<<16)+
+ (st->subQ.absolute_address.lba.lba3<< 8)+
+ st->subQ.absolute_address.lba.lba4,
+ (integer_t)
+ (st->subQ.relative_address.lba.lba1<<24)+
+ (st->subQ.relative_address.lba.lba2<<16)+
+ (st->subQ.relative_address.lba.lba3<< 8)+
+ st->subQ.relative_address.lba.lba4);
+ tgt->dev_info.cdrom.result_available = TRUE;
+ }
+ }
+ break;
+
+ case 'T':
+ /* "Get TH" */
+ if_it_can_do(read_toc) {
+ rc = scsi_read_toc(tgt, TRUE, 1, PAGE_SIZE, ior);
+ if (rc == SCSI_RET_SUCCESS) {
+ cdrom_toc_t *toc = (cdrom_toc_t *)tgt->cmd_ptr;
+ sprintf(buf, "toc header: %d %d %d",
+ (toc->len1 << 8) + toc->len2,
+ toc->first_track,
+ toc->last_track);
+ tgt->dev_info.cdrom.result_available = TRUE;
+ }
+ }
+ break;
+
+ case 'S':
+ /* "Get Status" */
+ if_it_can_do(get_status) {
+ rc = scsi_read_subchannel(tgt,
+ TRUE,
+ SCSI_CMD_RS_FMT_CURPOS,
+ 0,
+ ior);
+ if (rc == SCSI_RET_SUCCESS) {
+ cdrom_chan_curpos_t *st;
+ st = (cdrom_chan_curpos_t *)tgt->cmd_ptr;
+ decode_status(buf, st->audio_status);
+ tgt->dev_info.cdrom.result_available = TRUE;
+ }
+ }
+ break;
+ }
+ break;
+
+ case 'P':
+ switch (cmd[5]) {
+ case 'A':
+ /* "Play A startM startS startF endM endS endF" */
+ if_it_can_do(play_msf) {
+
+ int sm, ss, sf, em, es, ef;
+
+ sscanf(&cmd[7], "%d %d %d %d %d %d",
+ &sm, &ss, &sf, &em, &es, &ef);
+
+ rc = scsi_play_audio_msf(tgt,
+ sm, ss, sf,
+ em, es, ef,
+ ior);
+ }
+ break;
+
+ case 'T':
+ /* "Play TI startT startI endT endI" */
+ if_it_can_do(play_ti) {
+
+ int st, si, et, ei;
+
+ sscanf(&cmd[8], "%d %d %d %d",
+ &st, &si, &et, &ei);
+
+ rc = scsi_play_audio_track_index(tgt,
+ st, si, et, ei, ior);
+ }
+ break;
+ }
+ break;
+
+ case 'R':
+ /* "Resume" */
+ if_it_can_do(pause_resume)
+ rc = scsi_pause_resume(tgt, FALSE, ior);
+ break;
+
+ case 'S':
+ switch (cmd[2]) {
+
+ case 'a':
+ /* "Start" */
+ rc = scsi_start_unit(tgt, SCSI_CMD_SS_START, ior);
+ break;
+
+ case 'o':
+ /* "Stop" */
+ if_it_can_do(pause_resume)
+ rc = scsi_pause_resume(tgt, TRUE, ior);
+ break;
+
+ case 't':
+ /* "Set V chan0vol chan1vol chan2vol chan3vol" */
+ if_it_can_do(volume_control) {
+
+ int v0, v1, v2, v3;
+ cdrom_audio_page_t au, *aup;
+
+ rc = scsi_mode_sense(tgt,
+ SCSI_CD_AUDIO_PAGE,
+ sizeof(au),
+ ior);
+ if (rc == SCSI_RET_SUCCESS) {
+
+ sscanf(&cmd[6], "%d %d %d %d",
+ &v0, &v1, &v2, &v3);
+
+ aup = (cdrom_audio_page_t *) tgt->cmd_ptr;
+ au = *aup;
+ /* au.h.bdesc ... */
+ au.vol0 = v0;
+ au.vol1 = v1;
+ au.vol2 = v2;
+ au.vol3 = v3;
+ au.imm = 1;
+ au.aprv = 0;
+
+ zero_ior( ior );
+
+ rc = scsi2_mode_select(tgt, FALSE,
+ &au, sizeof(au), ior);
+ }
+ }
+ break;
+ }
+ break;
+
+ case 'T':
+ /* "Toc MSF|ABS trackno" */
+ if_it_can_do(read_toc) {
+
+ int t, m;
+
+ sscanf(&cmd[8], "%d", &t);
+ rc = scsi_read_toc( tgt, cmd[4]=='M', t, PAGE_SIZE, ior);
+
+ if (rc == SCSI_RET_SUCCESS) {
+
+ cdrom_toc_t *toc = (cdrom_toc_t *)tgt->cmd_ptr;
+
+ sprintf(buf, "TOC from track %d:\n", t);
+ m = (toc->len1 << 8) + toc->len2;
+ m -= 4; /* header */
+ for (t = 0; m > 0; t++, m -= sizeof(struct cdrom_toc_desc)) {
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "%d %d %d %d %d %d\n",
+ toc->descs[t].control,
+ toc->descs[t].adr,
+ toc->descs[t].trackno,
+ (integer_t)toc->descs[t].absolute_address.msf.minute,
+ (integer_t)toc->descs[t].absolute_address.msf.second,
+ (integer_t)toc->descs[t].absolute_address.msf.frame);
+ else
+ sprintf(buf, "%d %d %d %d\n",
+ toc->descs[t].control,
+ toc->descs[t].adr,
+ toc->descs[t].trackno,
+ (toc->descs[t].absolute_address.lba.lba1<<24)+
+ (toc->descs[t].absolute_address.lba.lba2<<16)+
+ (toc->descs[t].absolute_address.lba.lba3<<8)+
+ toc->descs[t].absolute_address.lba.lba4);
+ }
+ tgt->dev_info.cdrom.result_available = TRUE;
+ }
+ }
+ break;
+ }
+
+ if (rc == SCSI_RET_SUCCESS)
+ ret = D_SUCCESS;
+
+ /* We are stateless, but.. */
+ if (rc == SCSI_RET_NEED_SENSE) {
+ zero_ior( ior );
+ tgt->ior = ior;
+ scsi_request_sense(tgt, ior, 0);
+ iowait(ior);
+ if (scsi_check_sense_data(tgt, tgt->cmd_ptr))
+ scsi_print_sense_data(tgt->cmd_ptr);
+ }
+
+ io_req_free(ior);
+ return ret;
+}
+
+private char st_invalid [] = "Drive would not say";
+private char st_playing [] = "Playing";
+private char st_paused [] = "Suspended";
+private char st_complete[] = "Done playing";
+private char st_error [] = "Stopped in error";
+private char st_nothing [] = "Idle";
+
+private void
+decode_status(
+ char *buf,
+ unsigned char audio_status)
+{
+ switch (audio_status) {
+ case SCSI_CDST_INVALID:
+ sprintf(buf, st_invalid); break;
+ case SCSI_CDST_PLAYING:
+ sprintf(buf, st_playing); break;
+ case SCSI_CDST_PAUSED:
+ sprintf(buf, st_paused); break;
+ case SCSI_CDST_COMPLETED:
+ sprintf(buf, st_complete); break;
+ case SCSI_CDST_ERROR:
+ sprintf(buf, st_error); break;
+ case SCSI_CDST_NO_STATUS:
+ sprintf(buf, st_nothing); break;
+ }
+}
+
+/* some vendor specific use this instead */
+private void
+decode_status_1(
+ char *buf,
+ unsigned char audio_status)
+{
+ switch (audio_status) {
+ case 0: sprintf(buf, st_playing ); break;
+ case 1:
+ case 2: sprintf(buf, st_paused ); break;
+ case 3: sprintf(buf, st_complete ); break;
+ default:
+ sprintf(buf, "Unknown status" ); break;
+ }
+}
+
+
+private void
+curse_the_vendor(
+ red_list_t *list,
+ boolean_t not_really)
+{
+ if (not_really) return;
+
+ printf("%s\n%s\n%s\n%s\n",
+ "The CDROM you use is not fully SCSI-2 compliant.",
+ "We invite You to contact Your vendor and ask",
+ "that they provide You with a firmware upgrade.",
+ "Here is a list of some known deficiencies");
+
+ printf("Vendor: %s Product: %s.. Revision: %s..\n",
+ list->vendor, list->product, list->rev);
+
+#define check(x,y,z) \
+ if (list->x) printf("Command code x%x %s not supported\n", y, z);
+
+ check(can_play_audio, SCSI_CMD_PLAY_AUDIO, "PLAY_AUDIO");
+ check(eject, SCSI_CMD_START_STOP_UNIT,
+ "START_STOP_UNIT, flag EJECT(0x2) in byte 5");
+ check(current_position, SCSI_CMD_READ_SUBCH, "READ_SUBCHANNEL");
+ check(read_toc, SCSI_CMD_READ_TOC, "READ_TOC");
+/* check(get_status, ...); duplicate of current_position */
+ check(play_msf, SCSI_CMD_PLAY_AUDIO_MSF, "PLAY_AUDIO_MSF");
+ check(play_ti, SCSI_CMD_PLAY_AUDIO_TI, "PLAY_AUDIO_TRACK_INDEX");
+ check(pause_resume, SCSI_CMD_PAUSE_RESUME, "PAUSE_RESUME");
+ check(volume_control, SCSI_CMD_MODE_SELECT,
+ "MODE_SELECT, AUDIO page(0xe)");
+
+#undef check
+ printf("Will work around these problems...\n");
+}
+
+/*
+ * Ancillaries
+ */
+cd_strategy(ior)
+ register io_req_t ior;
+{
+ return rz_simpleq_strategy( ior, cd_start);
+}
+
+void cd_start( tgt, done)
+ target_info_t *tgt;
+ boolean_t done;
+{
+ io_req_t ior;
+
+ ior = tgt->ior;
+ if (done && ior) {
+ tgt->ior = 0;
+ iodone(ior);
+ return;
+ }
+ panic("cd start"); /* uhu? */
+}
+
+/*
+ * When the hardware cannot
+ */
+private scsi_ret_t
+op_not_supported(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ /*
+ * The command is not implemented, no way around it
+ */
+ sprintf(tgt->dev_info.cdrom.result, unsupported);
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+/****************************************/
+/* Vendor Specific Operations */
+/****************************************/
+
+ /* DEC RRD42 */
+
+#define SCSI_CMD_DEC_SET_ADDRESS_FORMAT 0xc0
+# define scsi_cmd_saf_fmt scsi_cmd_xfer_len_2
+
+#define SCSI_CMD_DEC_PLAYBACK_STATUS 0xc4
+typedef struct {
+ unsigned char xxx;
+ BITFIELD_2(unsigned char,
+ is_msf: 1,
+ xxx1: 7);
+ unsigned char data_len1;
+ unsigned char data_len0;
+ unsigned char audio_status;
+ BITFIELD_2(unsigned char,
+ control : 4,
+ xxx2 : 4);
+ cdrom_addr_t address;
+ BITFIELD_2(unsigned char,
+ chan0_select : 4,
+ xxx3 : 4);
+ unsigned char chan0_volume;
+ BITFIELD_2(unsigned char,
+ chan1_select : 4,
+ xxx4 : 4);
+ unsigned char chan1_volume;
+ BITFIELD_2(unsigned char,
+ chan2_select : 4,
+ xxx5 : 4);
+ unsigned char chan2_volume;
+ BITFIELD_2(unsigned char,
+ chan3_select : 4,
+ xxx6 : 4);
+ unsigned char chan3_volume;
+} dec_playback_status_t;
+
+#define SCSI_CMD_DEC_PLAYBACK_CONTROL 0xc9
+typedef struct {
+ unsigned char xxx0;
+ BITFIELD_2(unsigned char,
+ fmt : 1,
+ xxx1 : 7);
+ unsigned char xxx[8];
+ BITFIELD_2(unsigned char,
+ chan0_select : 4,
+ xxx3 : 4);
+ unsigned char chan0_volume;
+ BITFIELD_2(unsigned char,
+ chan1_select : 4,
+ xxx4 : 4);
+ unsigned char chan1_volume;
+ BITFIELD_2(unsigned char,
+ chan2_select : 4,
+ xxx5 : 4);
+ unsigned char chan2_volume;
+ BITFIELD_2(unsigned char,
+ chan3_select : 4,
+ xxx6 : 4);
+ unsigned char chan3_volume;
+} dec_playback_control_t;
+
+
+#if 0
+
+private scsi_ret_t
+rrd42_status(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_ret_t rc;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_command_group_2 c;
+ dec_playback_status_t *st;
+
+ /* We might have to specify addressing fmt */
+ if (cmd[4] == 'P') {
+ scsi_command_group_2 saf;
+
+ bzero(&saf, sizeof(saf));
+ saf.scsi_cmd_code = SCSI_CMD_DEC_SET_ADDRESS_FORMAT;
+ saf.scsi_cmd_saf_fmt = (cmd[13] == 'A') ? 0 : 1;
+
+ rc = cdrom_vendor_specific(tgt, &saf, 0, 0, 0, ior);
+
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ zero_ior( ior );
+ }
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_DEC_PLAYBACK_STATUS;
+ c.scsi_cmd_xfer_len_2 = sizeof(*st);
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior);
+
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ st = (dec_playback_status_t *) tgt->cmd_ptr;
+
+ if (cmd[4] == 'S')
+ decode_status( buf, st->audio_status+0x11 );
+ else {
+ if (st->is_msf)
+ sprintf(buf, "MSF Position %d %d %d",
+ (integer_t)st->address.msf.minute,
+ (integer_t)st->address.msf.second,
+ (integer_t)st->address.msf.frame);
+ else
+ sprintf(buf, "ABS Position %d", (integer_t)
+ (st->address.lba.lba1<<24)+
+ (st->address.lba.lba2<<16)+
+ (st->address.lba.lba3<< 8)+
+ st->address.lba.lba4);
+ }
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return rc;
+}
+#endif
+
+private scsi_ret_t
+rrd42_set_volume(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ dec_playback_control_t req;
+ int v0, v1, v2, v3;
+
+ sscanf(&cmd[6], "%d %d %d %d", &v0, &v1, &v2, &v3);
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_DEC_PLAYBACK_CONTROL;
+ c.scsi_cmd_xfer_len_2 = sizeof(req);
+ bzero(&req, sizeof(req));
+ if (v0) {
+ req.chan0_select = 1;
+ req.chan0_volume = v0;
+ }
+ if (v1) {
+ req.chan1_select = 2;
+ req.chan1_volume = v1;
+ }
+ if (v2) {
+ req.chan2_select = 4;
+ req.chan2_volume = v2;
+ }
+ if (v3) {
+ req.chan3_select = 8;
+ req.chan3_volume = v3;
+ }
+ return cdrom_vendor_specific(tgt, &c, &req, sizeof(req), 0, ior);
+}
+
+ /* NEC CD-ROM */
+
+#define SCSI_CMD_NEC_READ_TOC 0xde
+typedef struct {
+ unsigned char xxx[9];
+ unsigned char first_track;
+ unsigned char xxx1[9];
+ unsigned char last_track;
+ unsigned char xxx2[9];
+ unsigned char lead_out_addr[3];
+ struct {
+ BITFIELD_2(unsigned char,
+ adr : 4,
+ ctrl : 4);
+ unsigned char xxx3[6];
+ unsigned char address[3];
+ } track_info[1]; /* VARSIZE */
+} nec_toc_data_t;
+
+#define SCSI_CMD_NEC_SEEK_TRK 0xd8
+#define SCSI_CMD_NEC_PLAY_AUDIO 0xd9
+#define SCSI_CMD_NEC_PAUSE 0xda
+#define SCSI_CMD_NEC_EJECT 0xdc
+
+#define SCSI_CMD_NEC_READ_SUBCH_Q 0xdd
+typedef struct {
+ unsigned char audio_status; /* see decode_status_1 */
+ BITFIELD_2(unsigned char,
+ ctrl : 4,
+ xxx1 : 4);
+ unsigned char trackno;
+ unsigned char indexno;
+ unsigned char relative_address[3];
+ unsigned char absolute_address[3];
+} nec_subch_data_t;
+
+/*
+ * Reserved bits in byte1
+ */
+#define NEC_LR_PLAY_MODE 0x01 /* RelAdr bit overload */
+#define NEC_LR_STEREO 0x02 /* mono/stereo */
+
+/*
+ * Vendor specific bits in the control byte.
+ * NEC uses them to specify the addressing mode
+ */
+#define NEC_CTRL_A_ABS 0x00 /* XXX not sure about this */
+#define NEC_CTRL_A_MSF 0x40 /* min/sec/frame */
+#define NEC_CTRL_A_TI 0x80 /* track/index */
+#define NEC_CTRL_A_CURRENT 0xc0 /* same as last specified */
+
+private scsi_ret_t
+nec_eject(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_NEC_EJECT;
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+nec_subchannel(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ nec_subch_data_t *st;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_NEC_READ_SUBCH_Q;
+ c.scsi_cmd_lun_and_relbit = sizeof(*st); /* Sic! */
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ st = (nec_subch_data_t *) tgt->cmd_ptr;
+
+ /* Status or Position ? */
+
+ if (cmd[4] == 'S') {
+ decode_status_1( buf, st->audio_status);
+ } else {
+
+ /* XXX can it do ABS addressing e.g. 'logical' ? */
+
+ sprintf(buf, "MSF Position %d %d %d %d %d %d",
+ (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */
+ (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */
+ }
+
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+private scsi_ret_t
+nec_read_toc(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ nec_toc_data_t *t;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+ int first, last, i;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_NEC_READ_TOC;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO;
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, 512/*XXX*/, ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ t = (nec_toc_data_t *) tgt->cmd_ptr;
+
+ first = bcd_to_decimal(t->first_track);
+ last = bcd_to_decimal(t->last_track);
+
+ /*
+ * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all
+ */
+ if (cmd[0] == 'G') {
+ sprintf(buf, "toc header: %d %d %d",
+ sizeof(*t) + sizeof(t->track_info) * (last - first - 1),
+ first, last);
+ goto out;
+ }
+
+ /*
+ * The whole shebang
+ */
+ sscanf(&cmd[8], "%d", &i);
+ sprintf(buf, "TOC from track %d:\n", i);
+
+ last -= first;
+ i -= first;
+ while ((i >= 0) && (i <= last)) {
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "%d %d %d %d %d %d\n",
+ t->track_info[i].ctrl,
+ t->track_info[i].adr,
+ first + i,
+ bcd_to_decimal(t->track_info[i].address[0]),
+ bcd_to_decimal(t->track_info[i].address[1]),
+ bcd_to_decimal(t->track_info[i].address[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "%d %d %d %d\n",
+ t->track_info[i].ctrl,
+ t->track_info[i].adr,
+ first + i,
+ bcd_to_decimal(t->track_info[i].address[0]) * 10000 +
+ bcd_to_decimal(t->track_info[i].address[1]) * 100 +
+ bcd_to_decimal(t->track_info[i].address[2]));
+ i++;
+ }
+ /* To know how long the last track is */
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "%d %d %d %d %d %d\n",
+ 0, 1, 0xaa /* User expects this */,
+ bcd_to_decimal(t->lead_out_addr[0]),
+ bcd_to_decimal(t->lead_out_addr[1]),
+ bcd_to_decimal(t->lead_out_addr[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "%d %d %d %d\n",
+ 0, 1, 0xaa /* User expects this */,
+ bcd_to_decimal(t->lead_out_addr[0]) * 10000 +
+ bcd_to_decimal(t->lead_out_addr[1]) * 100 +
+ bcd_to_decimal(t->lead_out_addr[2]));
+out:
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+
+private scsi_ret_t
+nec_play(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ int sm, ss, sf, em, es, ef;
+ int st, si, et, ei;
+ scsi_ret_t rc;
+
+ /*
+ * Seek to desired position
+ */
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_NEC_SEEK_TRK;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE;
+
+ /*
+ * Play_msf or Play_ti
+ */
+ if (cmd[5] == 'A') {
+ /* "Play A startM startS startF endM endS endF" */
+
+ sscanf(&cmd[7], "%d %d %d %d %d %d",
+ &sm, &ss, &sf, &em, &es, &ef);
+
+ c.scsi_cmd_lba1 = decimal_to_bcd(sm);
+ c.scsi_cmd_lba2 = decimal_to_bcd(ss);
+ c.scsi_cmd_lba3 = decimal_to_bcd(sf);
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF;
+
+ } else {
+ /* "Play TI startT startI endT endI" */
+
+ sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei);
+
+ c.scsi_cmd_lba1 = decimal_to_bcd(st);
+ c.scsi_cmd_lba2 = decimal_to_bcd(si);
+ c.scsi_cmd_lba3 = 0;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI;
+
+ }
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ /*
+ * Now ask it to play until..
+ */
+ zero_ior( ior );
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_NEC_PLAY_AUDIO;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO;
+
+ if (cmd[5] == 'A') {
+ c.scsi_cmd_lba1 = decimal_to_bcd(em);
+ c.scsi_cmd_lba2 = decimal_to_bcd(es);
+ c.scsi_cmd_lba3 = decimal_to_bcd(ef);
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF;
+ } else {
+ c.scsi_cmd_lba1 = decimal_to_bcd(et);
+ c.scsi_cmd_lba2 = decimal_to_bcd(ei);
+ c.scsi_cmd_lba3 = 0;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI;
+ }
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+nec_pause_resume(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ /*
+ * "Resume" or "Stop"
+ */
+ if (cmd[0] == 'R') {
+ c.scsi_cmd_code = SCSI_CMD_NEC_PLAY_AUDIO;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_CURRENT;
+ } else {
+ c.scsi_cmd_code = SCSI_CMD_NEC_PAUSE;
+ }
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+ /* TOSHIBA CD-ROM DRIVE:XM 3232 */
+
+#define SCSI_CMD_TOSHIBA_SEEK_TRK 0xc0
+#define SCSI_CMD_TOSHIBA_PLAY_AUDIO 0xc1
+#define SCSI_CMD_TOSHIBA_PAUSE_AUDIO 0xc2
+#define SCSI_CMD_TOSHIBA_EJECT 0xc4
+
+#define SCSI_CMD_TOSHIBA_READ_SUBCH_Q 0xc6
+typedef nec_subch_data_t toshiba_subch_data_t;
+/* audio status -> decode_status_1 */
+
+#define SCSI_CMD_TOSHIBA_READ_TOC_ENTRY 0xc7
+typedef struct {
+ unsigned char first_track;
+ unsigned char last_track;
+ unsigned char xxx[2];
+} toshiba_toc_header_t;
+typedef struct {
+ unsigned char address[4];
+} toshiba_toc_data_t;
+
+
+private scsi_ret_t
+toshiba_eject(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_EJECT;
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+toshiba_subchannel(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ toshiba_subch_data_t *st;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_SUBCH_Q;
+ c.scsi_cmd_lun_and_relbit = sizeof(*st); /* Sic! */
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*st), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ st = (toshiba_subch_data_t *) tgt->cmd_ptr;
+
+ /* Status or Position ? */
+
+ if (cmd[4] == 'S') {
+ decode_status_1( buf, st->audio_status);
+ } else {
+
+ /* XXX can it do ABS addressing e.g. 'logical' ? */
+
+ sprintf(buf, "MSF Position %d %d %d %d %d %d",
+ (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */
+ (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */
+ }
+
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+private scsi_ret_t
+toshiba_read_toc(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ toshiba_toc_data_t *t;
+ toshiba_toc_header_t *th;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+ int first, last, i;
+
+ /* TOC header first */
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY;
+ c.scsi_cmd_lun_and_relbit = 0;
+ c.scsi_cmd_lba1 = 0;
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*th), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ th = (toshiba_toc_header_t *) tgt->cmd_ptr;
+
+ first = bcd_to_decimal(th->first_track);
+ last = bcd_to_decimal(th->last_track);
+
+ /*
+ * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all
+ */
+ if (cmd[0] == 'G') {
+ sprintf(buf, "toc header: %d %d %d",
+ sizeof(*th) + sizeof(*t) * (last - first + 1),
+ first, last);
+ goto out;
+ }
+
+ /*
+ * The whole shebang
+ */
+ sscanf(&cmd[8], "%d", &i);
+ sprintf(buf, "TOC from track %d:\n", i);
+
+ while (i <= last) {
+ bzero(&c, sizeof(c));
+
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY;
+ c.scsi_cmd_lun_and_relbit = 2;
+ c.scsi_cmd_lba1 = decimal_to_bcd(i);
+
+ zero_ior( ior );
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*t), ior);
+ if (rc != SCSI_RET_SUCCESS) break;
+
+ t = (toshiba_toc_data_t *) tgt->cmd_ptr;
+
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "0 0 %d %d %d %d\n",
+ i,
+ bcd_to_decimal(t->address[0]),
+ bcd_to_decimal(t->address[1]),
+ bcd_to_decimal(t->address[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "0 0 %d %d\n",
+ i,
+ bcd_to_decimal(t->address[0]) * 10000 +
+ bcd_to_decimal(t->address[1]) * 100 +
+ bcd_to_decimal(t->address[2]));
+ i++;
+ }
+
+ /* Must simulate the lead-out track */
+ bzero(&c, sizeof(c));
+
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_READ_TOC_ENTRY;
+ c.scsi_cmd_lun_and_relbit = 1;
+ c.scsi_cmd_lba1 = 0;
+
+ zero_ior( ior );
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(*t), ior);
+ if (rc != SCSI_RET_SUCCESS) goto out;
+
+ t = (toshiba_toc_data_t *) tgt->cmd_ptr;
+
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "0 0 %d %d %d %d\n",
+ i,
+ bcd_to_decimal(t->address[0]),
+ bcd_to_decimal(t->address[1]),
+ bcd_to_decimal(t->address[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "0 0 %d %d\n",
+ i,
+ bcd_to_decimal(t->address[0]) * 10000 +
+ bcd_to_decimal(t->address[1]) * 100 +
+ bcd_to_decimal(t->address[2]));
+ i++;
+
+out:
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+
+private scsi_ret_t
+toshiba_play(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ int sm, ss, sf, em, es, ef;
+ int st, si, et, ei;
+ scsi_ret_t rc;
+
+ /*
+ * Seek to desired position
+ */
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_SEEK_TRK;
+
+ /*
+ * Play_msf or Play_ti
+ */
+ if (cmd[5] == 'A') {
+ /* "Play A startM startS startF endM endS endF" */
+
+ sscanf(&cmd[7], "%d %d %d %d %d %d",
+ &sm, &ss, &sf, &em, &es, &ef);
+
+ c.scsi_cmd_lba1 = decimal_to_bcd(sm);
+ c.scsi_cmd_lba2 = decimal_to_bcd(ss);
+ c.scsi_cmd_lba3 = decimal_to_bcd(sf);
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF;
+
+ } else {
+ /* "Play TI startT startI endT endI" */
+
+ sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei);
+
+ c.scsi_cmd_lba1 = decimal_to_bcd(st);
+ c.scsi_cmd_lba2 = decimal_to_bcd(si);
+ c.scsi_cmd_lba3 = 0;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI;
+
+ }
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ /*
+ * Now ask it to play until..
+ */
+ zero_ior( ior );
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PLAY_AUDIO;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO;
+
+ if (cmd[5] == 'A') {
+ c.scsi_cmd_lba1 = decimal_to_bcd(em);
+ c.scsi_cmd_lba2 = decimal_to_bcd(es);
+ c.scsi_cmd_lba3 = decimal_to_bcd(ef);
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_MSF;
+ } else {
+ c.scsi_cmd_lba1 = decimal_to_bcd(et);
+ c.scsi_cmd_lba2 = decimal_to_bcd(ei);
+ c.scsi_cmd_lba3 = 0;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_TI;
+ }
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+toshiba_pause_resume(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ /*
+ * "Resume" or "Stop"
+ */
+ if (cmd[0] == 'R') {
+ /* ???? would have to remember last cmd ???? */
+/* broken ! */
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PLAY_AUDIO;
+ c.scsi_cmd_lun_and_relbit = NEC_LR_PLAY_MODE|NEC_LR_STEREO;
+ c.scsi_cmd_ctrl_byte = NEC_CTRL_A_CURRENT;
+ } else {
+ c.scsi_cmd_code = SCSI_CMD_TOSHIBA_PAUSE_AUDIO;
+ }
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+
+#if 0
+ /* I have info on these drives, but no drive to test */
+
+ /* PIONEER DRM-600 */
+
+#define SCSI_CMD_PIONEER_EJECT 0xc0
+
+#define SCSI_CMD_PIONEER_READ_TOC 0xc1
+typedef struct {
+ unsigned char first_track;
+ unsigned char last_track;
+ unsigned char xxx[2];
+} pioneer_toc_hdr_t;
+typedef struct {
+ unsigned char ctrl;
+ unsigned char address[3];
+} pioneer_toc_info_t;
+
+#define SCSI_CMD_PIONEER_READ_SUBCH 0xc2
+typedef struct {
+ BITFIELD_2(unsigned char,
+ ctrl : 4,
+ xxx1 : 4);
+ unsigned char trackno;
+ unsigned char indexno;
+ unsigned char relative_address[3];
+ unsigned char absolute_address[3];
+} pioneer_subch_data_t;
+
+#define SCSI_CMD_PIONEER_SEEK_TRK 0xc8
+#define SCSI_CMD_PIONEER_PLAY_AUDIO 0xc9
+#define SCSI_CMD_PIONEER_PAUSE 0xca
+
+#define SCSI_CMD_PIONEER_AUDIO_STATUS 0xcc
+typedef struct {
+ unsigned char audio_status;
+ unsigned char xxx[5];
+} pioneer_status_t;
+
+/*
+ * Reserved bits in byte1
+ */
+#define PIONEER_LR_END_ADDR 0x10
+#define PIONEER_LR_PAUSE 0x10
+#define PIONEER_LR_RESUME 0x00
+
+/*
+ * Vendor specific bits in the control byte.
+ */
+#define PIONEER_CTRL_TH 0x00 /* TOC header */
+#define PIONEER_CTRL_TE 0x80 /* one TOC entry */
+#define PIONEER_CTRL_LO 0x40 /* lead-out track info */
+
+#define PIONEER_CTRL_A_MSF 0x40 /* min/sec/frame addr */
+
+private scsi_ret_t
+pioneer_eject(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_EJECT;
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+pioneer_position(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ scsi_ret_t rc;
+ char *buf = tgt->dev_info.cdrom.result;
+ pioneer_subch_data_t *st;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_SUBCH;
+ c.scsi_cmd_xfer_len_2 = sizeof(pioneer_subch_data_t); /* 9 bytes */
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_subch_data_t), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ st = (pioneer_subch_data_t *) tgt->cmd_ptr;
+
+ /* XXX can it do ABS addressing e.g. 'logical' ? */
+
+ sprintf(buf, "MSF Position %d %d %d %d %d %d",
+ (integer_t)bcd_to_decimal(st->absolute_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->absolute_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->absolute_address[2]), /* frm */
+ (integer_t)bcd_to_decimal(st->relative_address[0]), /* min */
+ (integer_t)bcd_to_decimal(st->relative_address[1]), /* sec */
+ (integer_t)bcd_to_decimal(st->relative_address[2])); /* frm */
+
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+private scsi_ret_t
+pioneer_toc(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ pioneer_toc_hdr_t *th;
+ pioneer_toc_info_t *t;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+ int first, last, i;
+
+ /* Read header first */
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC;
+ c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_hdr_t);
+ c.scsi_cmd_ctrl_byte = PIONEER_CTRL_TH;
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_hdr_t), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ th = (pioneer_toc_hdr_t *)tgt->cmd_ptr;
+ first = bcd_to_decimal(th->first_track);
+ last = bcd_to_decimal(th->last_track);
+
+ /*
+ * "Get TH" wants summary, "TOC MSF|ABS from_track" wants all
+ */
+ if (cmd[0] == 'G') {
+ sprintf(buf, "toc header: %d %d %d", 0, first, last);
+ goto out;
+ }
+
+ /*
+ * Must do it one track at a time
+ */
+ sscanf(&cmd[8], "%d", &i);
+ sprintf(buf, "TOC from track %d:\n", i);
+
+ for ( ; i <= last; i++) {
+ zero_ior(ior);
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC;
+ c.scsi_cmd_lba4 = decimal_to_bcd(i);
+ c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_info_t);
+ c.scsi_cmd_ctrl_byte = PIONEER_CTRL_TE;
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_info_t), ior);
+ if (rc != SCSI_RET_SUCCESS) break;
+
+ t = (pioneer_toc_info_t *)tgt->cmd_ptr;
+
+ buf += strlen(buf);
+ if (cmd[4] == 'M')
+ sprintf(buf, "%d %d %d %d %d %d\n",
+ t->ctrl, 0, i,
+ bcd_to_decimal(t->address[0]),
+ bcd_to_decimal(t->address[1]),
+ bcd_to_decimal(t->address[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "%d %d %d %d\n",
+ t->ctrl, 0, i,
+ bcd_to_decimal(t->address[0]) * 10000 +
+ bcd_to_decimal(t->address[1]) * 100 +
+ bcd_to_decimal(t->address[2]));
+ }
+ /* To know how long the last track is */
+ zero_ior(ior);
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_READ_TOC;
+ c.scsi_cmd_xfer_len_2 = sizeof(pioneer_toc_info_t);
+ c.scsi_cmd_ctrl_byte = PIONEER_CTRL_LO;
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_toc_info_t), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ buf += strlen(buf);
+ t = (pioneer_toc_info_t *)tgt->cmd_ptr;
+ if (cmd[4] == 'M')
+ sprintf(buf, "%d %d %d %d %d %d\n",
+ t->ctrl, 0, 0xaa /* User expects this */,
+ bcd_to_decimal(t->address[0]),
+ bcd_to_decimal(t->address[1]),
+ bcd_to_decimal(t->address[2]));
+ else
+/* THIS IS WRONG */
+ sprintf(buf, "%d %d %d %d\n",
+ t->ctrl, 0, 0xaa /* User expects this */,
+ bcd_to_decimal(t->address[0]) * 10000 +
+ bcd_to_decimal(t->address[1]) * 100 +
+ bcd_to_decimal(t->address[2]));
+
+out:
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+private scsi_ret_t
+pioneer_status(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ pioneer_status_t *st;
+ char *buf = tgt->dev_info.cdrom.result;
+ scsi_ret_t rc;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_AUDIO_STATUS;
+ c.scsi_cmd_xfer_len_2 = sizeof(pioneer_status_t); /* 6 bytes */
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, sizeof(pioneer_status_t), ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ st = (pioneer_status_t*) tgt->cmd_ptr;
+ decode_status_1( buf, st->audio_status);
+
+ tgt->dev_info.cdrom.result_available = TRUE;
+ return SCSI_RET_SUCCESS;
+}
+
+private scsi_ret_t
+pioneer_play(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+ int sm, ss, sf, em, es, ef;
+ int st, si, et, ei;
+ scsi_ret_t rc;
+
+ /*
+ * Seek to desired position
+ */
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_SEEK_TRK;
+ /*
+ * Play_msf or Play_ti
+ */
+ if (cmd[5] == 'A') {
+ /* "Play A startM startS startF endM endS endF" */
+
+ sscanf(&cmd[7], "%d %d %d %d %d %d",
+ &sm, &ss, &sf, &em, &es, &ef);
+
+ c.scsi_cmd_lba2 = decimal_to_bcd(sm);
+ c.scsi_cmd_lba3 = decimal_to_bcd(ss);
+ c.scsi_cmd_lba4 = decimal_to_bcd(sf);
+ c.scsi_cmd_ctrl_byte = PIONEER_CTRL_A_MSF;
+
+ } else {
+ /* "Play TI startT startI endT endI" */
+
+ sscanf(&cmd[8], "%d %d %d %d", &st, &si, &et, &ei);
+
+ c.scsi_cmd_lba3 = decimal_to_bcd(st);
+ c.scsi_cmd_lba4 = decimal_to_bcd(si);
+ c.scsi_cmd_ctrl_byte = 0x80; /* Pure speculation!! */
+
+ }
+
+ rc = cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+ if (rc != SCSI_RET_SUCCESS) return rc;
+
+ /*
+ * Now ask it to play until..
+ */
+ zero_ior( ior );
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_PLAY_AUDIO;
+ c.scsi_cmd_lun_and_relbit = PIONEER_LR_END_ADDR;
+
+ if (cmd[5] == 'A') {
+ c.scsi_cmd_lba2 = decimal_to_bcd(em);
+ c.scsi_cmd_lba3 = decimal_to_bcd(es);
+ c.scsi_cmd_lba4 = decimal_to_bcd(ef);
+ c.scsi_cmd_ctrl_byte = PIONEER_CTRL_A_MSF;
+ } else {
+ c.scsi_cmd_lba3 = decimal_to_bcd(et);
+ c.scsi_cmd_lba4 = decimal_to_bcd(ei);
+ c.scsi_cmd_ctrl_byte = 0x80; /* Pure speculation! */
+ }
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+private scsi_ret_t
+pioneer_pause_resume(
+ target_info_t *tgt,
+ char *cmd,
+ io_req_t ior)
+{
+ scsi_command_group_2 c;
+
+ bzero(&c, sizeof(c));
+ c.scsi_cmd_code = SCSI_CMD_PIONEER_PAUSE;
+ /*
+ * "Resume" or "Stop"
+ */
+ if (cmd[0] == 'S')
+ c.scsi_cmd_lun_and_relbit = PIONEER_LR_PAUSE;
+ else
+ c.scsi_cmd_lun_and_relbit = PIONEER_LR_RESUME;
+
+ return cdrom_vendor_specific(tgt, &c, 0, 0, 0, ior);
+}
+
+ /* DENON DRD-253 */
+
+#define SCSI_CMD_DENON_PLAY_AUDIO 0x22
+#define SCSI_CMD_DENON_EJECT 0xe6
+#define SCSI_CMD_DENON_PAUSE_AUDIO 0xe7
+#define SCSI_CMD_DENON_READ_TOC 0xe9
+#define SCSI_CMD_DENON_READ_SUBCH 0xeb
+
+
+ /* HITACHI 1750 */
+
+#define SCSI_CMD_HITACHI_PLAY_AUDIO_MSF 0xe0
+#define SCSI_CMD_HITACHI_PAUSE_AUDIO 0xe1
+#define SCSI_CMD_HITACHI_EJECT 0xe4
+#define SCSI_CMD_HITACHI_READ_SUBCH 0xe5
+#define SCSI_CMD_HITACHI_READ_TOC 0xe8
+
+#endif
+
+/*
+ * Tabulate all of the above
+ */
+private red_list_t cdrom_exceptions[] = {
+
+#if 0
+ For documentation purposes, here are some SCSI-2 compliant drives:
+
+ Vendor Product Rev Comments
+
+ "SONY " "CD-ROMCDU-541 " "2.6a" The NeXT drive
+#endif
+
+ /* vendor, product, rev */
+ /* can_play_audio */
+ /* eject */
+ /* current_position */
+ /* read_toc */
+ /* get_status */
+ /* play_msf */
+ /* play_ti */
+ /* pause_resume */
+ /* volume_control */
+
+ /* We have seen a "RRD42(C)DEC " "4.5d" */
+ { "DEC ", "RRD42", "",
+ 0, 0, 0, 0, 0, 0, 0, 0, rrd42_set_volume },
+
+ /* We have seen a "CD-ROM DRIVE:84 " "1.0 " */
+ { "NEC ", "CD-ROM DRIVE:84", "",
+ op_not_supported, nec_eject, nec_subchannel, nec_read_toc,
+ nec_subchannel, nec_play, nec_play, nec_pause_resume,
+ op_not_supported },
+
+ /* We have seen a "CD-ROM DRIVE:XM " "3232" */
+ { "TOSHIBA ", "CD-ROM DRIVE:XM", "32",
+ op_not_supported, toshiba_eject, toshiba_subchannel, toshiba_read_toc,
+ toshiba_subchannel, toshiba_play, toshiba_play, toshiba_pause_resume,
+ op_not_supported },
+
+ { "TOSHIBA ", "CD-ROM DRIVE:XM", "33",
+ op_not_supported, toshiba_eject, toshiba_subchannel, toshiba_read_toc,
+ toshiba_subchannel, toshiba_play, toshiba_play, toshiba_pause_resume,
+ op_not_supported },
+
+#if 0
+ { "PIONEER ", "???????DRM-6", "",
+ op_not_supported, pioneer_eject, pioneer_position, pioneer_toc,
+ pioneer_status, pioneer_play, pioneer_play, pioneer_pause_resume,
+ op_not_supported },
+
+ { "DENON ", "DRD 25X", "", ...},
+ { "HITACHI ", "CDR 1750S", "", ...},
+ { "HITACHI ", "CDR 1650S", "", ...},
+ { "HITACHI ", "CDR 3650", "", ...},
+
+#endif
+
+ /* Zero terminate this list */
+ { 0, }
+};
+
+private void
+check_red_list(
+ target_info_t *tgt,
+ scsi2_inquiry_data_t *inq)
+
+{
+ red_list_t *list;
+
+ for (list = &cdrom_exceptions[0]; list->vendor; list++) {
+
+ /*
+ * Prefix-Match all strings
+ */
+ if ((strncmp(list->vendor, (const char *)inq->vendor_id,
+ strlen(list->vendor)) == 0) &&
+ (strncmp(list->product, (const char *)inq->product_id,
+ strlen(list->product)) == 0) &&
+ (strncmp(list->rev, (const char *)inq->product_rev,
+ strlen(list->rev)) == 0)) {
+ /*
+ * One of them..
+ */
+ if (tgt->dev_info.cdrom.violates_standards != list) {
+ tgt->dev_info.cdrom.violates_standards = list;
+ curse_the_vendor( list, TRUE );
+ }
+ return;
+ }
+ }
+}
+#endif /* NSCSI > 0 */