/* * 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 #include #include /* spl definitions */ #include #include #include #include #include #include #include #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 */