/* @(#)scsi_cdr.c 1.160 12/03/16 Copyright 1995-2012 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)scsi_cdr.c 1.160 12/03/16 Copyright 1995-2012 J. Schilling"; #endif /* * SCSI command functions for cdrecord * covering pre-MMC standard functions up to MMC-2 * * Copyright (c) 1995-2012 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ /* * NOTICE: The Philips CDD 521 has several firmware bugs. * One of them is not to respond to a SCSI selection * within 200ms if the general load on the * SCSI bus is high. To deal with this problem * most of the SCSI commands are send with the * SCG_CMD_RETRY flag enabled. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scsimmc.h" #include "cdrecord.h" #define strbeg(s1, s2) (strstr((s2), (s1)) == (s2)) EXPORT BOOL unit_ready __PR((SCSI *scgp)); EXPORT BOOL wait_unit_ready __PR((SCSI *scgp, int secs)); EXPORT BOOL scsi_in_progress __PR((SCSI *scgp)); EXPORT BOOL cdr_underrun __PR((SCSI *scgp)); EXPORT int test_unit_ready __PR((SCSI *scgp)); EXPORT int rezero_unit __PR((SCSI *scgp)); EXPORT int request_sense __PR((SCSI *scgp)); EXPORT int request_sense_b __PR((SCSI *scgp, caddr_t bp, int cnt)); EXPORT int inquiry __PR((SCSI *scgp, caddr_t, int)); EXPORT int read_capacity __PR((SCSI *scgp)); EXPORT void print_capacity __PR((SCSI *scgp, FILE *f)); EXPORT int scsi_load_unload __PR((SCSI *scgp, int)); EXPORT int scsi_prevent_removal __PR((SCSI *scgp, int)); EXPORT int scsi_start_stop_unit __PR((SCSI *scgp, int, int, BOOL immed)); EXPORT int scsi_set_speed __PR((SCSI *scgp, int readspeed, int writespeed, int rotctl)); EXPORT int scsi_get_speed __PR((SCSI *scgp, int *readspeedp, int *writespeedp)); EXPORT int qic02 __PR((SCSI *scgp, int)); EXPORT int write_xscsi __PR((SCSI *scgp, caddr_t, long, long, int)); EXPORT int write_xg0 __PR((SCSI *scgp, caddr_t, long, long, int)); EXPORT int write_xg1 __PR((SCSI *scgp, caddr_t, long, long, int)); EXPORT int write_xg5 __PR((SCSI *scgp, caddr_t, long, long, int)); EXPORT int seek_scsi __PR((SCSI *scgp, long addr)); EXPORT int seek_g0 __PR((SCSI *scgp, long addr)); EXPORT int seek_g1 __PR((SCSI *scgp, long addr)); EXPORT int scsi_flush_cache __PR((SCSI *scgp, BOOL immed)); EXPORT int read_buffer __PR((SCSI *scgp, caddr_t bp, int cnt, int mode)); EXPORT int write_buffer __PR((SCSI *scgp, char *buffer, long length, int mode, int bufferid, long offset)); EXPORT int read_subchannel __PR((SCSI *scgp, caddr_t bp, int track, int cnt, int msf, int subq, int fmt)); EXPORT int read_toc __PR((SCSI *scgp, caddr_t, int, int, int, int)); EXPORT int read_toc_philips __PR((SCSI *scgp, caddr_t, int, int, int, int)); EXPORT int read_header __PR((SCSI *scgp, caddr_t, long, int, int)); EXPORT int read_disk_info __PR((SCSI *scgp, caddr_t, int)); EXPORT int read_track_info __PR((SCSI *scgp, caddr_t, int type, int addr, int cnt)); EXPORT int get_trackinfo __PR((SCSI *scgp, caddr_t, int type, int addr, int cnt)); EXPORT int read_rzone_info __PR((SCSI *scgp, caddr_t bp, int cnt)); EXPORT int reserve_tr_rzone __PR((SCSI *scgp, long size)); EXPORT int read_dvd_structure __PR((SCSI *scgp, caddr_t bp, int cnt, int mt, int addr, int layer, int fmt)); EXPORT int send_dvd_structure __PR((SCSI *scgp, caddr_t bp, int cnt, int fmt)); EXPORT int send_opc __PR((SCSI *scgp, caddr_t, int cnt, int doopc)); EXPORT int read_track_info_philips __PR((SCSI *scgp, caddr_t, int, int)); EXPORT int scsi_close_tr_session __PR((SCSI *scgp, int type, int track, BOOL immed)); EXPORT int read_master_cue __PR((SCSI *scgp, caddr_t bp, int sheet, int cnt)); EXPORT int send_cue_sheet __PR((SCSI *scgp, caddr_t bp, long size)); EXPORT int read_buff_cap __PR((SCSI *scgp, long *, long *)); EXPORT int scsi_blank __PR((SCSI *scgp, long addr, int blanktype, BOOL immed)); EXPORT BOOL allow_atapi __PR((SCSI *scgp, BOOL new)); EXPORT int mode_select __PR((SCSI *scgp, Uchar *, int, int, int)); EXPORT int mode_sense __PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf)); EXPORT int mode_select_sg0 __PR((SCSI *scgp, Uchar *, int, int, int)); EXPORT int mode_sense_sg0 __PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf)); EXPORT int mode_select_g0 __PR((SCSI *scgp, Uchar *, int, int, int)); EXPORT int mode_select_g1 __PR((SCSI *scgp, Uchar *, int, int, int)); EXPORT int mode_sense_g0 __PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf)); EXPORT int mode_sense_g1 __PR((SCSI *scgp, Uchar *dp, int cnt, int page, int pcf)); EXPORT int read_tochdr __PR((SCSI *scgp, cdr_t *, int *, int *)); EXPORT int read_cdtext __PR((SCSI *scgp)); EXPORT int read_trackinfo __PR((SCSI *scgp, int, long *, struct msf *, int *, int *, int *)); EXPORT int read_B0 __PR((SCSI *scgp, BOOL isbcd, long *b0p, long *lop)); EXPORT int read_session_offset __PR((SCSI *scgp, long *)); EXPORT int read_session_offset_philips __PR((SCSI *scgp, long *)); EXPORT int sense_secsize __PR((SCSI *scgp, int current)); EXPORT int select_secsize __PR((SCSI *scgp, int)); EXPORT BOOL is_cddrive __PR((SCSI *scgp)); EXPORT BOOL is_unknown_dev __PR((SCSI *scgp)); EXPORT int read_scsi __PR((SCSI *scgp, caddr_t, long, int)); EXPORT int read_g0 __PR((SCSI *scgp, caddr_t, long, int)); EXPORT int read_g1 __PR((SCSI *scgp, caddr_t, long, int)); EXPORT BOOL getdev __PR((SCSI *scgp, BOOL)); EXPORT void printinq __PR((SCSI *scgp, FILE *f)); EXPORT void printdev __PR((SCSI *scgp)); EXPORT BOOL do_inquiry __PR((SCSI *scgp, BOOL)); EXPORT BOOL recovery_needed __PR((SCSI *scgp, cdr_t *)); EXPORT int scsi_load __PR((SCSI *scgp, cdr_t *)); EXPORT int scsi_unload __PR((SCSI *scgp, cdr_t *)); EXPORT int scsi_cdr_write __PR((SCSI *scgp, caddr_t bp, long sectaddr, long size, int blocks, BOOL islast)); EXPORT struct cd_mode_page_2A * mmc_cap __PR((SCSI *scgp, Uchar *modep)); EXPORT void mmc_getval __PR((struct cd_mode_page_2A *mp, BOOL *cdrrp, BOOL *cdwrp, BOOL *cdrrwp, BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp)); EXPORT BOOL is_mmc __PR((SCSI *scgp, BOOL *cdwp, BOOL *dvdwp)); EXPORT BOOL mmc_check __PR((SCSI *scgp, BOOL *cdrrp, BOOL *cdwrp, BOOL *cdrrwp, BOOL *cdwrwp, BOOL *dvdp, BOOL *dvdwp)); LOCAL void print_speed __PR((char *fmt, int val)); EXPORT void print_capabilities __PR((SCSI *scgp)); extern int verify __PR((SCSI *scgp, long start, int count, long *bad_block)); EXPORT BOOL unit_ready(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; if (test_unit_ready(scgp) >= 0) /* alles OK */ return (TRUE); else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */ return (FALSE); if (scg_sense_key(scgp) == SC_UNIT_ATTENTION) { if (test_unit_ready(scgp) >= 0) /* alles OK */ return (TRUE); } if ((scg_cmd_status(scgp) & ST_BUSY) != 0) { /* * Busy/reservation_conflict */ usleep(500000); if (test_unit_ready(scgp) >= 0) /* alles OK */ return (TRUE); } if (scg_sense_key(scgp) == -1) { /* non extended Sense */ if (scg_sense_code(scgp) == 4) /* NOT_READY */ return (FALSE); return (TRUE); } /* FALSE wenn NOT_READY */ return (scg_sense_key(scgp) != SC_NOT_READY); } EXPORT BOOL wait_unit_ready(scgp, secs) SCSI *scgp; int secs; { int i; int c; int k; int ret; int err; seterrno(0); scgp->silent++; ret = test_unit_ready(scgp); /* eat up unit attention */ if (ret < 0) { err = geterrno(); if (err == EPERM || err == EACCES) { scgp->silent--; return (FALSE); } ret = test_unit_ready(scgp); /* got power on condition? */ } scgp->silent--; if (ret >= 0) /* success that's enough */ return (TRUE); scgp->silent++; for (i = 0; i < secs && (ret = test_unit_ready(scgp)) < 0; i++) { if (scgp->scmd->scb.busy != 0) { sleep(1); continue; } c = scg_sense_code(scgp); k = scg_sense_key(scgp); /* * Abort quickly if it does not make sense to wait. * 0x30 == Cannot read medium * 0x3A == Medium not present */ if ((k == SC_NOT_READY && (c == 0x3A || c == 0x30)) || (k == SC_MEDIUM_ERROR)) { if (scgp->silent <= 1) scg_printerr(scgp); scgp->silent--; return (FALSE); } sleep(1); } scgp->silent--; if (ret < 0) return (FALSE); return (TRUE); } EXPORT BOOL scsi_in_progress(scgp) SCSI *scgp; { if (scg_sense_key(scgp) == SC_NOT_READY && /* * Logigal unit not ready operation/long_write in progress */ scg_sense_code(scgp) == 0x04 && (scg_sense_qual(scgp) == 0x04 || /* CyberDr. "format in progress"*/ scg_sense_qual(scgp) == 0x07 || /* "operation in progress" */ scg_sense_qual(scgp) == 0x08)) { /* "long write in progress" */ return (TRUE); } else { if (scgp->silent <= 1) scg_printerr(scgp); } return (FALSE); } EXPORT BOOL cdr_underrun(scgp) SCSI *scgp; { if ((scg_sense_key(scgp) != SC_ILLEGAL_REQUEST && scg_sense_key(scgp) != SC_MEDIUM_ERROR)) return (FALSE); if ((scg_sense_code(scgp) == 0x21 && (scg_sense_qual(scgp) == 0x00 || /* logical block address out of range */ scg_sense_qual(scgp) == 0x02)) || /* invalid address for write */ (scg_sense_code(scgp) == 0x0C && scg_sense_qual(scgp) == 0x09)) { /* write error - loss of streaming */ return (TRUE); } /* * XXX Bei manchen Brennern kommt mach dem der Brennvorgang bereits * XXX eine Weile gelaufen ist ein 5/24/0 Invalid field in CDB. * XXX Daher sollte man testen ob schon geschrieben wurde... */ return (FALSE); } EXPORT int test_unit_ready(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)0; scmd->size = 0; scmd->flags = SCG_DISRE_ENA | (scgp->silent ? SCG_SILENT:0); scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_TEST_UNIT_READY; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scgp->cmdname = "test unit ready"; return (scg_cmd(scgp)); } EXPORT int rezero_unit(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)0; scmd->size = 0; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_REZERO_UNIT; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scgp->cmdname = "rezero unit"; return (scg_cmd(scgp)); } EXPORT int request_sense(scgp) SCSI *scgp; { char sensebuf[CCS_SENSE_LEN]; char *cmdsave; cmdsave = scgp->cmdname; if (request_sense_b(scgp, sensebuf, sizeof (sensebuf)) < 0) return (-1); scgp->cmdname = cmdsave; scg_prsense((Uchar *)sensebuf, CCS_SENSE_LEN - scg_getresid(scgp)); return (0); } EXPORT int request_sense_b(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_REQUEST_SENSE; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.count = cnt; scgp->cmdname = "request_sense"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int inquiry(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes(bp, cnt, '\0'); fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_INQUIRY; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.count = cnt; scgp->cmdname = "inquiry"; if (scg_cmd(scgp) < 0) return (-1); if (scgp->verbose) scg_prbytes(_("Inquiry Data :"), (Uchar *)bp, cnt - scg_getresid(scgp)); return (0); } EXPORT int read_capacity(scgp) SCSI *scgp; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)scgp->cap; scmd->size = sizeof (struct scsi_capacity); scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x25; /* Read Capacity */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdblen(&scmd->cdb.g1_cdb, 0); /* Full Media */ scgp->cmdname = "read capacity"; if (scg_cmd(scgp) < 0) { return (-1); } else { long cbsize; long cbaddr; /* * c_bsize & c_baddr are signed Int32_t * so we use signed int conversion here. */ cbsize = a_to_4_byte(&scgp->cap->c_bsize); cbaddr = a_to_4_byte(&scgp->cap->c_baddr); scgp->cap->c_bsize = cbsize; scgp->cap->c_baddr = cbaddr; } return (0); } EXPORT void print_capacity(scgp, f) SCSI *scgp; FILE *f; { long kb; long mb; long prmb; double dkb; dkb = (scgp->cap->c_baddr+1.0) * (scgp->cap->c_bsize/1024.0); kb = dkb; mb = dkb / 1024.0; prmb = dkb / 1000.0 * 1.024; fprintf(f, _("Capacity: %ld Blocks = %ld kBytes = %ld MBytes = %ld prMB\n"), (long)scgp->cap->c_baddr+1, kb, mb, prmb); fprintf(f, _("Sectorsize: %ld Bytes\n"), (long)scgp->cap->c_bsize); } EXPORT int scsi_load_unload(scgp, load) SCSI *scgp; int load; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g5_cdb.cmd = 0xA6; scmd->cdb.g5_cdb.lun = scg_lun(scgp); scmd->cdb.g5_cdb.addr[1] = load?3:2; scmd->cdb.g5_cdb.count[2] = 0; /* slot # */ scgp->cmdname = "medium load/unload"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int scsi_prevent_removal(scgp, prevent) SCSI *scgp; int prevent; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = 0x1E; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.count = prevent & 1; scgp->cmdname = "prevent/allow medium removal"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int scsi_start_stop_unit(scgp, flg, loej, immed) SCSI *scgp; int flg; int loej; BOOL immed; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = 0x1B; /* Start Stop Unit */ scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.count = (flg ? 1:0) | (loej ? 2:0); if (immed) scmd->cdb.cmd_cdb[1] |= 0x01; scgp->cmdname = "start/stop unit"; return (scg_cmd(scgp)); } EXPORT int scsi_set_speed(scgp, readspeed, writespeed, rotctl) SCSI *scgp; int readspeed; int writespeed; int rotctl; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g5_cdb.cmd = 0xBB; scmd->cdb.g5_cdb.lun = scg_lun(scgp); if (readspeed < 0) i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], 0xFFFF); else i_to_2_byte(&scmd->cdb.g5_cdb.addr[0], readspeed); if (writespeed < 0) i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], 0xFFFF); else i_to_2_byte(&scmd->cdb.g5_cdb.addr[2], writespeed); scmd->cdb.cmd_cdb[1] |= rotctl & 0x03; scgp->cmdname = "set cd speed"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int scsi_get_speed(scgp, readspeedp, writespeedp) SCSI *scgp; int *readspeedp; int *writespeedp; { struct cd_mode_page_2A *mp; Uchar m[256]; int val; scgp->silent++; mp = mmc_cap(scgp, m); /* Get MMC capabilities in allocated mp */ scgp->silent--; if (mp == NULL) return (-1); /* Pre SCSI-3/mmc drive */ val = a_to_u_2_byte(mp->cur_read_speed); if (readspeedp) *readspeedp = val; if (mp->p_len >= 28) val = a_to_u_2_byte(mp->v3_cur_write_speed); else val = a_to_u_2_byte(mp->cur_write_speed); if (writespeedp) *writespeedp = val; return (0); } EXPORT int qic02(scgp, cmd) SCSI *scgp; int cmd; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)0; scmd->size = 0; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = DEF_SENSE_LEN; scmd->cdb.g0_cdb.cmd = 0x0D; /* qic02 Sysgen SC4000 */ scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.mid_addr = cmd; scgp->cmdname = "qic 02"; return (scg_cmd(scgp)); } #define G0_MAXADDR 0x1FFFFFL EXPORT int write_xscsi(scgp, bp, addr, size, cnt) SCSI *scgp; caddr_t bp; /* address of buffer */ long addr; /* disk address (sector) to put */ long size; /* number of bytes to transfer */ int cnt; /* sectorcount */ { if (addr <= G0_MAXADDR) return (write_xg0(scgp, bp, addr, size, cnt)); else return (write_xg1(scgp, bp, addr, size, cnt)); } EXPORT int write_xg0(scgp, bp, addr, size, cnt) SCSI *scgp; caddr_t bp; /* address of buffer */ long addr; /* disk address (sector) to put */ long size; /* number of bytes to transfer */ int cnt; /* sectorcount */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = size; scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; /* scmd->flags = SCG_DISRE_ENA;*/ scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_WRITE; scmd->cdb.g0_cdb.lun = scg_lun(scgp); g0_cdbaddr(&scmd->cdb.g0_cdb, addr); scmd->cdb.g0_cdb.count = cnt; scgp->cmdname = "write_g0"; if (scg_cmd(scgp) < 0) return (-1); return (size - scg_getresid(scgp)); } EXPORT int write_xg1(scgp, bp, addr, size, cnt) SCSI *scgp; caddr_t bp; /* address of buffer */ long addr; /* disk address (sector) to put */ long size; /* number of bytes to transfer */ int cnt; /* sectorcount */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = size; scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; /* scmd->flags = SCG_DISRE_ENA;*/ scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = SC_EWRITE; scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdbaddr(&scmd->cdb.g1_cdb, addr); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "write_g1"; if (scg_cmd(scgp) < 0) return (-1); return (size - scg_getresid(scgp)); } EXPORT int write_xg5(scgp, bp, addr, size, cnt) SCSI *scgp; caddr_t bp; /* address of buffer */ long addr; /* disk address (sector) to put */ long size; /* number of bytes to transfer */ int cnt; /* sectorcount */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = size; scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; /* scmd->flags = SCG_DISRE_ENA;*/ scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g5_cdb.cmd = 0xAA; scmd->cdb.g5_cdb.lun = scg_lun(scgp); g5_cdbaddr(&scmd->cdb.g5_cdb, addr); g5_cdblen(&scmd->cdb.g5_cdb, cnt); scgp->cmdname = "write_g5"; if (scg_cmd(scgp) < 0) return (-1); return (size - scg_getresid(scgp)); } EXPORT int seek_scsi(scgp, addr) SCSI *scgp; long addr; { if (addr <= G0_MAXADDR) return (seek_g0(scgp, addr)); else return (seek_g1(scgp, addr)); } EXPORT int seek_g0(scgp, addr) SCSI *scgp; long addr; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = 0x0B; /* Seek */ scmd->cdb.g0_cdb.lun = scg_lun(scgp); g0_cdbaddr(&scmd->cdb.g0_cdb, addr); scgp->cmdname = "seek_g0"; return (scg_cmd(scgp)); } EXPORT int seek_g1(scgp, addr) SCSI *scgp; long addr; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x2B; /* Seek G1 */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdbaddr(&scmd->cdb.g1_cdb, addr); scgp->cmdname = "seek_g1"; return (scg_cmd(scgp)); } EXPORT int scsi_flush_cache(scgp, immed) SCSI *scgp; BOOL immed; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 2 * 60; /* Max: sizeof (CDR-cache)/150KB/s */ scmd->cdb.g1_cdb.cmd = 0x35; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (immed) scmd->cdb.cmd_cdb[1] |= 0x02; scgp->cmdname = "flush cache"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_buffer(scgp, bp, cnt, mode) SCSI *scgp; caddr_t bp; int cnt; int mode; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->dma_read = 1; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x3C; /* Read Buffer */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.cmd_cdb[1] |= (mode & 7); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read buffer"; return (scg_cmd(scgp)); } EXPORT int write_buffer(scgp, buffer, length, mode, bufferid, offset) SCSI *scgp; char *buffer; long length; int mode; int bufferid; long offset; { register struct scg_cmd *scmd = scgp->scmd; char *cdb; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = buffer; scmd->size = length; scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; cdb = (char *)scmd->cdb.cmd_cdb; cdb[0] = 0x3B; cdb[1] = mode & 7; cdb[2] = bufferid; cdb[3] = offset >> 16; cdb[4] = (offset >> 8) & 0xff; cdb[5] = offset & 0xff; cdb[6] = length >> 16; cdb[7] = (length >> 8) & 0xff; cdb[8] = length & 0xff; scgp->cmdname = "write_buffer"; if (scg_cmd(scgp) >= 0) return (1); return (0); } EXPORT int read_subchannel(scgp, bp, track, cnt, msf, subq, fmt) SCSI *scgp; caddr_t bp; int track; int cnt; int msf; int subq; int fmt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x42; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (msf) scmd->cdb.g1_cdb.res = 1; if (subq) scmd->cdb.g1_cdb.addr[0] = 0x40; scmd->cdb.g1_cdb.addr[1] = fmt; scmd->cdb.g1_cdb.res6 = track; g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read subchannel"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_toc(scgp, bp, track, cnt, msf, fmt) SCSI *scgp; caddr_t bp; int track; int cnt; int msf; int fmt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x43; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (msf) scmd->cdb.g1_cdb.res = 1; scmd->cdb.g1_cdb.addr[0] = fmt & 0x0F; scmd->cdb.g1_cdb.res6 = track; g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read toc"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_toc_philips(scgp, bp, track, cnt, msf, fmt) SCSI *scgp; caddr_t bp; int track; int cnt; int msf; int fmt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 4 * 60; /* May last 174s on a TEAC CD-R55S */ scmd->cdb.g1_cdb.cmd = 0x43; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (msf) scmd->cdb.g1_cdb.res = 1; scmd->cdb.g1_cdb.res6 = track; g1_cdblen(&scmd->cdb.g1_cdb, cnt); if (fmt & 1) scmd->cdb.g1_cdb.vu_96 = 1; if (fmt & 2) scmd->cdb.g1_cdb.vu_97 = 1; scgp->cmdname = "read toc"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_header(scgp, bp, addr, cnt, msf) SCSI *scgp; caddr_t bp; long addr; int cnt; int msf; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x44; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (msf) scmd->cdb.g1_cdb.res = 1; g1_cdbaddr(&scmd->cdb.g1_cdb, addr); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read header"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_disk_info(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 4 * 60; /* Needs up to 2 minutes */ scmd->cdb.g1_cdb.cmd = 0x51; scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read disk info"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_track_info(scgp, bp, type, addr, cnt) SCSI *scgp; caddr_t bp; int type; int addr; int cnt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 4 * 60; /* Needs up to 2 minutes */ scmd->cdb.g1_cdb.cmd = 0x52; scmd->cdb.g1_cdb.lun = scg_lun(scgp); /* scmd->cdb.cmd_cdb[1] = type & 0x03;*/ scmd->cdb.cmd_cdb[1] = type; g1_cdbaddr(&scmd->cdb.g1_cdb, addr); /* LBA/Track/Session */ g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read track info"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int get_trackinfo(scgp, bp, type, addr, cnt) SCSI *scgp; caddr_t bp; int type; int addr; int cnt; { int len; int ret; fillbytes(bp, cnt, '\0'); /* * Used to be 2 instead of 4 (now). But some Y2k ATAPI drives as used * by IOMEGA create a DMA overrun if we try to transfer only 2 bytes. */ if (read_track_info(scgp, bp, type, addr, 4) < 0) return (-1); len = a_to_u_2_byte(bp); len += 2; if (len > cnt) len = cnt; ret = read_track_info(scgp, bp, type, addr, len); #ifdef DEBUG if (lverbose > 1) scg_prbytes(_("Track info:"), (Uchar *)bp, len-scg_getresid(scgp)); #endif return (ret); } EXPORT int read_rzone_info(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { return (get_trackinfo(scgp, bp, TI_TYPE_LBA, 0, cnt)); } EXPORT int reserve_tr_rzone(scgp, size) SCSI *scgp; long size; /* number of blocks */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)0; scmd->size = 0; scmd->flags = SCG_DISRE_ENA|SCG_CMD_RETRY; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x53; scmd->cdb.g1_cdb.lun = scg_lun(scgp); i_to_4_byte(&scmd->cdb.cmd_cdb[5], size); scgp->cmdname = "reserve_track_rzone"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_dvd_structure(scgp, bp, cnt, mt, addr, layer, fmt) SCSI *scgp; caddr_t bp; int cnt; int mt; int addr; int layer; int fmt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */ scmd->cdb.g5_cdb.cmd = 0xAD; scmd->cdb.g5_cdb.lun = scg_lun(scgp); scmd->cdb.cmd_cdb[1] |= (mt & 0x0F); /* Media Type */ g5_cdbaddr(&scmd->cdb.g5_cdb, addr); g5_cdblen(&scmd->cdb.g5_cdb, cnt); scmd->cdb.g5_cdb.count[0] = layer; scmd->cdb.g5_cdb.count[1] = fmt; scgp->cmdname = "read dvd structure"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int send_dvd_structure(scgp, bp, cnt, fmt) SCSI *scgp; caddr_t bp; int cnt; int fmt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 4 * 60; /* Needs up to 2 minutes ??? */ scmd->cdb.g5_cdb.cmd = 0xBF; scmd->cdb.g5_cdb.lun = scg_lun(scgp); g5_cdblen(&scmd->cdb.g5_cdb, cnt); scmd->cdb.g5_cdb.count[0] = 0; scmd->cdb.g5_cdb.count[1] = fmt; scgp->cmdname = "send dvd structure"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int send_opc(scgp, bp, cnt, doopc) SCSI *scgp; caddr_t bp; int cnt; int doopc; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 60; scmd->cdb.g1_cdb.cmd = 0x54; scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.g1_cdb.reladr = doopc?1:0; g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "send opc"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_track_info_philips(scgp, bp, track, cnt) SCSI *scgp; caddr_t bp; int track; int cnt; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0xE5; scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdbaddr(&scmd->cdb.g1_cdb, track); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read track info"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int scsi_close_tr_session(scgp, type, track, immed) SCSI *scgp; int type; int track; BOOL immed; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 8 * 60; /* Needs up to 4 minutes */ scmd->cdb.g1_cdb.cmd = 0x5B; scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.g1_cdb.addr[0] = type; scmd->cdb.g1_cdb.addr[3] = track; if (immed) scmd->cdb.g1_cdb.reladr = 1; /* scmd->cdb.cmd_cdb[1] |= 0x01;*/ #ifdef nono scmd->cdb.g1_cdb.reladr = 1; /* IMM hack to test Mitsumi behaviour*/ #endif scgp->cmdname = "close track/session"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int read_master_cue(scgp, bp, sheet, cnt) SCSI *scgp; caddr_t bp; /* address of master cue sheet */ int sheet; /* Sheet number */ int cnt; /* Transfer count */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x59; /* Read master cue */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.g1_cdb.addr[2] = sheet; g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read master cue"; if (scg_cmd(scgp) < 0) return (-1); return (0); } EXPORT int send_cue_sheet(scgp, bp, size) SCSI *scgp; caddr_t bp; /* address of cue sheet buffer */ long size; /* number of bytes to transfer */ { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = size; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x5D; /* Send CUE sheet */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdblen(&scmd->cdb.g1_cdb, size); scgp->cmdname = "send_cue_sheet"; if (scg_cmd(scgp) < 0) return (-1); return (size - scmd->resid); } EXPORT int read_buff_cap(scgp, sp, fp) SCSI *scgp; long *sp; /* Size pointer */ long *fp; /* Free pointer */ { char resp[12]; Ulong freespace; Ulong bufsize; int per; register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)resp; scmd->size = sizeof (resp); scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x5C; /* Read buffer cap */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdblen(&scmd->cdb.g1_cdb, sizeof (resp)); scgp->cmdname = "read buffer cap"; if (scg_cmd(scgp) < 0) return (-1); bufsize = a_to_u_4_byte(&resp[4]); freespace = a_to_u_4_byte(&resp[8]); if (sp) *sp = bufsize; if (fp) *fp = freespace; if (scgp->verbose || (sp == 0 && fp == 0)) printf(_("BFree: %ld K BSize: %ld K\n"), freespace >> 10, bufsize >> 10); if (bufsize == 0) return (0); per = (100 * (bufsize - freespace)) / bufsize; if (per < 0) return (0); if (per > 100) return (100); return (per); } EXPORT int scsi_blank(scgp, addr, blanktype, immed) SCSI *scgp; long addr; int blanktype; BOOL immed; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->timeout = 160 * 60; /* full blank at 1x could take 80 minutes */ scmd->cdb.g5_cdb.cmd = 0xA1; /* Blank */ scmd->cdb.g0_cdb.high_addr = blanktype; g1_cdbaddr(&scmd->cdb.g5_cdb, addr); if (immed) scmd->cdb.g5_cdb.res |= 8; /* scmd->cdb.cmd_cdb[1] |= 0x10;*/ scgp->cmdname = "blank unit"; return (scg_cmd(scgp)); } /* * XXX First try to handle ATAPI: * XXX ATAPI cannot handle SCSI 6 byte commands. * XXX We try to simulate 6 byte mode sense/select. */ LOCAL BOOL is_atapi; EXPORT BOOL allow_atapi(scgp, new) SCSI *scgp; BOOL new; { BOOL old = is_atapi; Uchar mode[256]; if (new == old) return (old); scgp->silent++; /* * If a bad drive has been reset before, we may need to fire up two * test unit ready commands to clear status. */ (void) unit_ready(scgp); if (new && mode_sense_g1(scgp, mode, 8, 0x3F, 0) < 0) { /* All pages current */ new = FALSE; } scgp->silent--; is_atapi = new; return (old); } EXPORT int mode_select(scgp, dp, cnt, smp, pf) SCSI *scgp; Uchar *dp; int cnt; int smp; int pf; { if (is_atapi) return (mode_select_sg0(scgp, dp, cnt, smp, pf)); return (mode_select_g0(scgp, dp, cnt, smp, pf)); } EXPORT int mode_sense(scgp, dp, cnt, page, pcf) SCSI *scgp; Uchar *dp; int cnt; int page; int pcf; { if (is_atapi) return (mode_sense_sg0(scgp, dp, cnt, page, pcf)); return (mode_sense_g0(scgp, dp, cnt, page, pcf)); } /* * Simulate mode select g0 with mode select g1. */ EXPORT int mode_select_sg0(scgp, dp, cnt, smp, pf) SCSI *scgp; Uchar *dp; int cnt; int smp; int pf; { Uchar xmode[256+4]; int amt = cnt; if (amt < 1 || amt > 255) { /* XXX clear SCSI error codes ??? */ return (-1); } if (amt < 4) { /* Data length. medium type & VU */ amt += 1; } else { amt += 4; movebytes(&dp[4], &xmode[8], cnt-4); } xmode[0] = 0; xmode[1] = 0; xmode[2] = dp[1]; xmode[3] = dp[2]; xmode[4] = 0; xmode[5] = 0; i_to_2_byte(&xmode[6], (unsigned int)dp[3]); if (scgp->verbose) scg_prbytes(_("Mode Parameters (un-converted)"), dp, cnt); return (mode_select_g1(scgp, xmode, amt, smp, pf)); } /* * Simulate mode sense g0 with mode sense g1. */ EXPORT int mode_sense_sg0(scgp, dp, cnt, page, pcf) SCSI *scgp; Uchar *dp; int cnt; int page; int pcf; { Uchar xmode[256+4]; int amt = cnt; int len; if (amt < 1 || amt > 255) { /* XXX clear SCSI error codes ??? */ return (-1); } fillbytes((caddr_t)xmode, sizeof (xmode), '\0'); if (amt < 4) { /* Data length. medium type & VU */ amt += 1; } else { amt += 4; } if (mode_sense_g1(scgp, xmode, amt, page, pcf) < 0) return (-1); amt = cnt - scg_getresid(scgp); /* * For tests: Solaris 8 & LG CD-ROM always returns resid == amt */ /* amt = cnt;*/ if (amt > 4) movebytes(&xmode[8], &dp[4], amt-4); len = a_to_u_2_byte(xmode); if (len == 0) { dp[0] = 0; } else if (len < 6) { if (len > 2) len = 2; dp[0] = len; } else { dp[0] = len - 3; } dp[1] = xmode[2]; dp[2] = xmode[3]; len = a_to_u_2_byte(&xmode[6]); dp[3] = len; if (scgp->verbose) scg_prbytes(_("Mode Sense Data (converted)"), dp, amt); return (0); } EXPORT int mode_select_g0(scgp, dp, cnt, smp, pf) SCSI *scgp; Uchar *dp; int cnt; int smp; int pf; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)dp; scmd->size = cnt; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_MODE_SELECT; scmd->cdb.g0_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0; scmd->cdb.g0_cdb.count = cnt; if (scgp->verbose) { error("%s ", smp?_("Save"):_("Set ")); scg_prbytes(_("Mode Parameters"), dp, cnt); } scgp->cmdname = "mode select g0"; return (scg_cmd(scgp)); } EXPORT int mode_select_g1(scgp, dp, cnt, smp, pf) SCSI *scgp; Uchar *dp; int cnt; int smp; int pf; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)dp; scmd->size = cnt; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x55; scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.g0_cdb.high_addr = smp ? 1 : 0 | pf ? 0x10 : 0; g1_cdblen(&scmd->cdb.g1_cdb, cnt); if (scgp->verbose) { printf("%s ", smp?_("Save"):_("Set ")); scg_prbytes(_("Mode Parameters"), dp, cnt); } scgp->cmdname = "mode select g1"; return (scg_cmd(scgp)); } EXPORT int mode_sense_g0(scgp, dp, cnt, page, pcf) SCSI *scgp; Uchar *dp; int cnt; int page; int pcf; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)dp; scmd->size = 0xFF; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_MODE_SENSE; scmd->cdb.g0_cdb.lun = scg_lun(scgp); #ifdef nonono scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */ #endif scmd->cdb.g0_cdb.mid_addr = (page&0x3F) | ((pcf<<6)&0xC0); scmd->cdb.g0_cdb.count = page ? 0xFF : 24; scmd->cdb.g0_cdb.count = cnt; scgp->cmdname = "mode sense g0"; if (scg_cmd(scgp) < 0) return (-1); if (scgp->verbose) scg_prbytes(_("Mode Sense Data"), dp, cnt - scg_getresid(scgp)); return (0); } EXPORT int mode_sense_g1(scgp, dp, cnt, page, pcf) SCSI *scgp; Uchar *dp; int cnt; int page; int pcf; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)dp; scmd->size = cnt; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x5A; scmd->cdb.g1_cdb.lun = scg_lun(scgp); #ifdef nonono scmd->cdb.g0_cdb.high_addr = 1<<4; /* DBD Disable Block desc. */ #endif scmd->cdb.g1_cdb.addr[0] = (page&0x3F) | ((pcf<<6)&0xC0); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "mode sense g1"; if (scg_cmd(scgp) < 0) return (-1); if (scgp->verbose) scg_prbytes(_("Mode Sense Data"), dp, cnt - scg_getresid(scgp)); return (0); } struct trackdesc { Uchar res0; #if defined(_BIT_FIELDS_LTOH) /* Intel byteorder */ Ucbit control : 4; Ucbit adr : 4; #else /* Motorola byteorder */ Ucbit adr : 4; Ucbit control : 4; #endif Uchar track; Uchar res3; Uchar addr[4]; }; struct diskinfo { struct tocheader hd; struct trackdesc desc[1]; }; struct siheader { Uchar len[2]; Uchar finished; Uchar unfinished; }; struct sidesc { Uchar sess_number; Uchar res1; Uchar track; Uchar res3; Uchar addr[4]; }; struct sinfo { struct siheader hd; struct sidesc desc[1]; }; struct trackheader { Uchar mode; Uchar res[3]; Uchar addr[4]; }; #define TRM_ZERO 0 #define TRM_USER_ECC 1 /* 2048 bytes user data + 288 Bytes ECC/EDC */ #define TRM_USER 2 /* All user data (2336 bytes) */ EXPORT int read_tochdr(scgp, dp, fp, lp) SCSI *scgp; cdr_t *dp; int *fp; int *lp; { struct tocheader *tp; char xb[256]; int len; tp = (struct tocheader *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc(scgp, xb, 0, sizeof (struct tocheader), 0, FMT_TOC) < 0) { if (scgp->silent == 0) errmsgno(EX_BAD, _("Cannot read TOC header\n")); return (-1); } len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2; if (len >= 4) { if (fp) *fp = tp->first; if (lp) *lp = tp->last; return (0); } return (-1); } EXPORT int read_cdtext(scgp) SCSI *scgp; { struct tocheader *tp; char xb[256]; int len; char xxb[10000]; tp = (struct tocheader *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc(scgp, xb, 0, sizeof (struct tocheader), 0, FMT_CDTEXT) < 0) { if (scgp->silent == 0 || scgp->verbose > 0) errmsgno(EX_BAD, _("Cannot read CD-Text header\n")); return (-1); } len = a_to_u_2_byte(tp->len) + sizeof (struct tocheader)-2; printf(_("CD-Text len: %d\n"), len); if (read_toc(scgp, xxb, 0, len, 0, FMT_CDTEXT) < 0) { if (scgp->silent == 0) errmsgno(EX_BAD, _("Cannot read CD-Text\n")); return (-1); } { FILE *f = fileopen("cdtext.dat", "wctb"); filewrite(f, xxb, len); } return (0); } EXPORT int read_trackinfo(scgp, track, offp, msfp, adrp, controlp, modep) SCSI *scgp; int track; long *offp; struct msf *msfp; int *adrp; int *controlp; int *modep; { struct diskinfo *dp; char xb[256]; int len; long off; dp = (struct diskinfo *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc(scgp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC) < 0) { if (scgp->silent <= 0) errmsgno(EX_BAD, _("Cannot read TOC\n")); return (-1); } len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; if (len < (int)sizeof (struct diskinfo)) return (-1); off = a_to_4_byte(dp->desc[0].addr); if (offp) *offp = off; if (adrp) *adrp = dp->desc[0].adr; if (controlp) *controlp = dp->desc[0].control; if (msfp) { scgp->silent++; if (read_toc(scgp, xb, track, sizeof (struct diskinfo), 1, FMT_TOC) >= 0) { msfp->msf_min = dp->desc[0].addr[1]; msfp->msf_sec = dp->desc[0].addr[2]; msfp->msf_frame = dp->desc[0].addr[3]; } else if (read_toc(scgp, xb, track, sizeof (struct diskinfo), 0, FMT_TOC) >= 0) { /* * Some drives (e.g. the Philips CDD-522) don't support * to read the TOC in MSF mode. */ long moff = a_to_4_byte(dp->desc[0].addr); lba_to_msf(moff, msfp); } else { msfp->msf_min = 0; msfp->msf_sec = 0; msfp->msf_frame = 0; } scgp->silent--; } if (modep == NULL) return (0); if (track == 0xAA) { *modep = -1; return (0); } fillbytes((caddr_t)xb, sizeof (xb), '\0'); scgp->silent++; if (read_header(scgp, xb, off, 8, 0) >= 0) { *modep = xb[0]; } else if (read_track_info_philips(scgp, xb, track, 14) >= 0) { *modep = xb[0xb] & 0xF; } else { *modep = -1; } scgp->silent--; return (0); } EXPORT int read_B0(scgp, isbcd, b0p, lop) SCSI *scgp; BOOL isbcd; long *b0p; long *lop; { struct fdiskinfo *dp; struct ftrackdesc *tp; char xb[8192]; char *pe; int len; long l; dp = (struct fdiskinfo *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc_philips(scgp, xb, 1, sizeof (struct tocheader), 0, FMT_FULLTOC) < 0) { return (-1); } len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; if (len < (int)sizeof (struct fdiskinfo)) return (-1); if (read_toc_philips(scgp, xb, 1, len, 0, FMT_FULLTOC) < 0) { return (-1); } if (scgp->verbose) { scg_prbytes(_("TOC data: "), (Uchar *)xb, len > (int)sizeof (xb) - scg_getresid(scgp) ? sizeof (xb) - scg_getresid(scgp) : len); tp = &dp->desc[0]; pe = &xb[len]; while ((char *)tp < pe) { scg_prbytes(_("ENT: "), (Uchar *)tp, 11); tp++; } } tp = &dp->desc[0]; pe = &xb[len]; for (; (char *)tp < pe; tp++) { if (tp->sess_number != dp->hd.last) continue; if (tp->point != 0xB0) continue; if (scgp->verbose) scg_prbytes("B0: ", (Uchar *)tp, 11); if (isbcd) { l = msf_to_lba(from_bcd(tp->amin), from_bcd(tp->asec), from_bcd(tp->aframe), TRUE); } else { l = msf_to_lba(tp->amin, tp->asec, tp->aframe, TRUE); } if (b0p) *b0p = l; if (scgp->verbose) printf(_("B0 start: %ld\n"), l); if (isbcd) { l = msf_to_lba(from_bcd(tp->pmin), from_bcd(tp->psec), from_bcd(tp->pframe), TRUE); } else { l = msf_to_lba(tp->pmin, tp->psec, tp->pframe, TRUE); } if (scgp->verbose) printf(("B0 lout: %ld\n"), l); if (lop) *lop = l; return (0); } return (-1); } /* * Return address of first track in last session (SCSI-3/mmc version). */ EXPORT int read_session_offset(scgp, offp) SCSI *scgp; long *offp; { struct diskinfo *dp; char xb[256]; int len; dp = (struct diskinfo *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc(scgp, (caddr_t)xb, 0, sizeof (struct tocheader), 0, FMT_SINFO) < 0) return (-1); if (scgp->verbose) scg_prbytes(_("tocheader: "), (Uchar *)xb, sizeof (struct tocheader) - scg_getresid(scgp)); len = a_to_u_2_byte(dp->hd.len) + sizeof (struct tocheader)-2; if (len > (int)sizeof (xb)) { errmsgno(EX_BAD, _("Session info too big.\n")); return (-1); } if (read_toc(scgp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0) return (-1); if (scgp->verbose) scg_prbytes(_("tocheader: "), (Uchar *)xb, len - scg_getresid(scgp)); dp = (struct diskinfo *)xb; if (offp) *offp = a_to_u_4_byte(dp->desc[0].addr); return (0); } /* * Return address of first track in last session (pre SCSI-3 version). */ EXPORT int read_session_offset_philips(scgp, offp) SCSI *scgp; long *offp; { struct sinfo *sp; char xb[256]; int len; sp = (struct sinfo *)xb; fillbytes((caddr_t)xb, sizeof (xb), '\0'); if (read_toc_philips(scgp, (caddr_t)xb, 0, sizeof (struct siheader), 0, FMT_SINFO) < 0) return (-1); len = a_to_u_2_byte(sp->hd.len) + sizeof (struct siheader)-2; if (len > (int)sizeof (xb)) { errmsgno(EX_BAD, _("Session info too big.\n")); return (-1); } if (read_toc_philips(scgp, (caddr_t)xb, 0, len, 0, FMT_SINFO) < 0) return (-1); /* * Old drives return the number of finished sessions in first/finished * a descriptor is returned for each session. * New drives return the number of the first and last session * one descriptor for the last finished session is returned * as in SCSI-3 * In all cases the lowest session number is set to 1. */ sp = (struct sinfo *)xb; if (offp) *offp = a_to_u_4_byte(sp->desc[sp->hd.finished-1].addr); return (0); } EXPORT int sense_secsize(scgp, current) SCSI *scgp; int current; { Uchar mode[0x100]; Uchar *p; Uchar *ep; int len; int secsize = -1; scgp->silent++; (void) unit_ready(scgp); scgp->silent--; /* XXX Quick and dirty, musz verallgemeinert werden !!! */ fillbytes(mode, sizeof (mode), '\0'); scgp->silent++; len = sizeof (struct scsi_mode_header) + sizeof (struct scsi_mode_blockdesc); /* * Wenn wir hier get_mode_params() nehmen bekommen wir die Warnung: * Warning: controller returns wrong page 1 for All pages page (3F). */ if (mode_sense(scgp, mode, len, 0x3F, current?0:2) < 0) { fillbytes(mode, sizeof (mode), '\0'); if (mode_sense(scgp, mode, len, 0, current?0:2) < 0) { /* VU (block desc) */ scgp->silent--; return (-1); } } if (mode[3] == 8) { if (scgp->debug) { printf(_("Density: 0x%X\n"), mode[4]); printf(_("Blocks: %ld\n"), a_to_u_3_byte(&mode[5])); printf(_("Blocklen:%ld\n"), a_to_u_3_byte(&mode[9])); } secsize = a_to_u_3_byte(&mode[9]); } fillbytes(mode, sizeof (mode), '\0'); /* * The ACARD TECH AEC-7720 ATAPI<->SCSI adaptor * chokes if we try to transfer more than 0x40 bytes with * mode_sense of all pages. So try to avoid to run this * command if possible. */ if (scgp->debug && mode_sense(scgp, mode, 0xFE, 0x3F, current?0:2) >= 0) { /* All Pages */ ep = mode+mode[0]; /* Points to last byte of data */ p = &mode[4]; p += mode[3]; printf(_("Pages: ")); while (p < ep) { printf("0x%X ", *p&0x3F); p += p[1]+2; } printf("\n"); } scgp->silent--; return (secsize); } EXPORT int select_secsize(scgp, secsize) SCSI *scgp; int secsize; { struct scsi_mode_data md; int count = sizeof (struct scsi_mode_header) + sizeof (struct scsi_mode_blockdesc); (void) test_unit_ready(scgp); /* clear any error situation */ fillbytes((caddr_t)&md, sizeof (md), '\0'); md.header.blockdesc_len = 8; i_to_3_byte(md.blockdesc.lblen, secsize); return (mode_select(scgp, (Uchar *)&md, count, 0, scgp->inq->data_format >= 2)); } EXPORT BOOL is_cddrive(scgp) SCSI *scgp; { return (scgp->inq->type == INQ_ROMD || scgp->inq->type == INQ_WORM); } EXPORT BOOL is_unknown_dev(scgp) SCSI *scgp; { return (scgp->dev == DEV_UNKNOWN); } #ifndef DEBUG #define DEBUG #endif #ifdef DEBUG EXPORT int read_scsi(scgp, bp, addr, cnt) SCSI *scgp; caddr_t bp; long addr; int cnt; { if (addr <= G0_MAXADDR && cnt < 256 && !is_atapi) return (read_g0(scgp, bp, addr, cnt)); else return (read_g1(scgp, bp, addr, cnt)); } EXPORT int read_g0(scgp, bp, addr, cnt) SCSI *scgp; caddr_t bp; long addr; int cnt; { register struct scg_cmd *scmd = scgp->scmd; if (scgp->cap->c_bsize <= 0) raisecond("capacity_not_set", 0L); fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt*scgp->cap->c_bsize; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G0_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g0_cdb.cmd = SC_READ; scmd->cdb.g0_cdb.lun = scg_lun(scgp); g0_cdbaddr(&scmd->cdb.g0_cdb, addr); scmd->cdb.g0_cdb.count = cnt; /* scmd->cdb.g0_cdb.vu_56 = 1;*/ scgp->cmdname = "read_g0"; return (scg_cmd(scgp)); } EXPORT int read_g1(scgp, bp, addr, cnt) SCSI *scgp; caddr_t bp; long addr; int cnt; { register struct scg_cmd *scmd = scgp->scmd; if (scgp->cap->c_bsize <= 0) raisecond("capacity_not_set", 0L); fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = bp; scmd->size = cnt*scgp->cap->c_bsize; scmd->flags = SCG_RECV_DATA|SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = SC_EREAD; scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdbaddr(&scmd->cdb.g1_cdb, addr); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read_g1"; return (scg_cmd(scgp)); } #endif /* DEBUG */ EXPORT BOOL getdev(scgp, print) SCSI *scgp; BOOL print; { BOOL got_inquiry = TRUE; char vendor_info[8+1]; char prod_ident[16+1]; char prod_revision[4+1]; int inq_len = 0; register struct scg_cmd *scmd = scgp->scmd; register struct scsi_inquiry *inq = scgp->inq; fillbytes((caddr_t)inq, sizeof (*inq), '\0'); scgp->dev = DEV_UNKNOWN; scgp->silent++; (void) unit_ready(scgp); if (scmd->error >= SCG_FATAL && !(scmd->scb.chk && scmd->sense_count > 0)) { scgp->silent--; return (FALSE); } /* if (scmd->error < SCG_FATAL || scmd->scb.chk && scmd->sense_count > 0){*/ if (inquiry(scgp, (caddr_t)inq, sizeof (*inq)) < 0) { got_inquiry = FALSE; } else { inq_len = sizeof (*inq) - scg_getresid(scgp); } if (!got_inquiry) { if (scgp->verbose) { printf( _("error: %d scb.chk: %d sense_count: %d sense.code: 0x%x\n"), scmd->error, scmd->scb.chk, scmd->sense_count, scmd->sense.code); } /* * Folgende Kontroller kennen das Kommando * INQUIRY nicht: * * ADAPTEC ACB-4000, ACB-4010, ACB 4070 * SYSGEN SC4000 * * Leider reagieren ACB40X0 und ACB5500 identisch * wenn drive not ready (code == not ready), * sie sind dann nicht zu unterscheiden. */ if (scmd->scb.chk && scmd->sense_count == 4) { /* Test auf SYSGEN */ (void) qic02(scgp, 0x12); /* soft lock on */ if (qic02(scgp, 1) < 0) { /* soft lock off */ scgp->dev = DEV_ACB40X0; /* scgp->dev = acbdev();*/ } else { scgp->dev = DEV_SC4000; inq->type = INQ_SEQD; inq->removable = 1; } } } else if (scgp->verbose) { int i; int len = inq->add_len + 5; Uchar ibuf[256+5]; Uchar *ip = (Uchar *)inq; Uchar c; if (len > (int)sizeof (*inq) && inquiry(scgp, (caddr_t)ibuf, inq->add_len+5) >= 0) { len = inq->add_len+5 - scg_getresid(scgp); ip = ibuf; } else { len = sizeof (*inq); } printf(_("Inquiry Data : ")); for (i = 0; i < len; i++) { c = ip[i]; if (c >= ' ' && c < 0177) printf("%c", c); else printf("."); } printf("\n"); } strncpy(vendor_info, inq->inq_vendor_info, sizeof (inq->inq_vendor_info)); strncpy(prod_ident, inq->inq_prod_ident, sizeof (inq->inq_prod_ident)); strncpy(prod_revision, inq->inq_prod_revision, sizeof (inq->inq_prod_revision)); vendor_info[sizeof (inq->inq_vendor_info)] = '\0'; prod_ident[sizeof (inq->inq_prod_ident)] = '\0'; prod_revision[sizeof (inq->inq_prod_revision)] = '\0'; switch (inq->type) { case INQ_DASD: if (inq->add_len == 0 && inq->inq_vendor_info[0] != '\0') { Uchar *p; /* * NT-4.0 creates fake inquiry data for IDE disks. * Unfortunately, it does not set add_len wo we * check if vendor_info, prod_ident and prod_revision * contains valid chars for a CCS inquiry. */ if (inq_len >= 36) inq->add_len = 31; for (p = (Uchar *)&inq->inq_vendor_info[0]; p < (Uchar *)&inq->inq_prod_revision[4]; p++) { if (*p < 0x20 || *p > 0x7E) { inq->add_len = 0; break; } } } if (inq->add_len == 0) { if (scgp->dev == DEV_UNKNOWN && got_inquiry) { scgp->dev = DEV_ACB5500; strncpy(inq->inq_info_space, "ADAPTEC ACB-5500 FAKE", sizeof (inq->inq_info_space)); } else switch (scgp->dev) { case DEV_ACB40X0: strncpy(inq->inq_info_space, "ADAPTEC ACB-40X0 FAKE", sizeof (inq->inq_info_space)); break; case DEV_ACB4000: strncpy(inq->inq_info_space, "ADAPTEC ACB-4000 FAKE", sizeof (inq->inq_info_space)); break; case DEV_ACB4010: strncpy(inq->inq_info_space, "ADAPTEC ACB-4010 FAKE", sizeof (inq->inq_info_space)); break; case DEV_ACB4070: strncpy(inq->inq_info_space, "ADAPTEC ACB-4070 FAKE", sizeof (inq->inq_info_space)); break; } } else if (inq->add_len < 31) { scgp->dev = DEV_NON_CCS_DSK; } else if (strbeg("EMULEX", vendor_info)) { if (strbeg("MD21", prod_ident)) scgp->dev = DEV_MD21; if (strbeg("MD23", prod_ident)) scgp->dev = DEV_MD23; else scgp->dev = DEV_CCS_GENDISK; } else if (strbeg("ADAPTEC", vendor_info)) { if (strbeg("ACB-4520", prod_ident)) scgp->dev = DEV_ACB4520A; if (strbeg("ACB-4525", prod_ident)) scgp->dev = DEV_ACB4525; else scgp->dev = DEV_CCS_GENDISK; } else if (strbeg("SONY", vendor_info) && strbeg("SMO-C501", prod_ident)) { scgp->dev = DEV_SONY_SMO; } else { scgp->dev = DEV_CCS_GENDISK; } break; case INQ_SEQD: if (scgp->dev == DEV_SC4000) { strncpy(inq->inq_info_space, "SYSGEN SC4000 FAKE", sizeof (inq->inq_info_space)); } else if (inq->add_len == 0 && inq->removable && inq->ansi_version == 1) { scgp->dev = DEV_MT02; strncpy(inq->inq_info_space, "EMULEX MT02 FAKE", sizeof (inq->inq_info_space)); } break; /* case INQ_OPTD:*/ case INQ_ROMD: case INQ_WORM: if (strbeg("RXT-800S", prod_ident)) scgp->dev = DEV_RXT800S; /* * Start of CD-Recorders: */ if (strbeg("ACER", vendor_info)) { if (strbeg("CR-4020C", prod_ident)) scgp->dev = DEV_RICOH_RO_1420C; } else if (strbeg("CREATIVE", vendor_info)) { if (strbeg("CDR2000", prod_ident)) scgp->dev = DEV_RICOH_RO_1060C; } else if (strbeg("GRUNDIG", vendor_info)) { if (strbeg("CDR100IPW", prod_ident)) scgp->dev = DEV_CDD_2000; } else if (strbeg("JVC", vendor_info)) { if (strbeg("XR-W2001", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; else if (strbeg("XR-W2010", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; else if (strbeg("R2626", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; } else if (strbeg("MITSBISH", vendor_info)) { #ifdef XXXX_REALLY /* It's MMC compliant */ if (strbeg("CDRW226", prod_ident)) scgp->dev = DEV_MMC_CDRW; #else /* EMPTY */ #endif } else if (strbeg("MITSUMI", vendor_info)) { /* Don't know any product string */ scgp->dev = DEV_CDD_522; } else if (strbeg("OPTIMA", vendor_info)) { if (strbeg("CD-R 650", prod_ident)) scgp->dev = DEV_SONY_CDU_924; } else if (strbeg("PHILIPS", vendor_info) || strbeg("IMS", vendor_info) || strbeg("KODAK", vendor_info) || strbeg("HP", vendor_info)) { if (strbeg("CDD521/00", prod_ident)) scgp->dev = DEV_CDD_521_OLD; else if (strbeg("CDD521/02", prod_ident)) scgp->dev = DEV_CDD_521_OLD; /* PCD 200R? */ else if (strbeg("CDD521", prod_ident)) scgp->dev = DEV_CDD_521; if (strbeg("CDD522", prod_ident)) scgp->dev = DEV_CDD_522; if (strbeg("PCD225", prod_ident)) scgp->dev = DEV_CDD_522; if (strbeg("KHSW/OB", prod_ident)) /* PCD600 */ scgp->dev = DEV_PCD_600; if (strbeg("CDR-240", prod_ident)) scgp->dev = DEV_CDD_2000; if (strbeg("CDD20", prod_ident)) scgp->dev = DEV_CDD_2000; if (strbeg("CDD26", prod_ident)) scgp->dev = DEV_CDD_2600; if (strbeg("C4324/C4325", prod_ident)) scgp->dev = DEV_CDD_2000; if (strbeg("CD-Writer 6020", prod_ident)) scgp->dev = DEV_CDD_2600; } else if (strbeg("PINNACLE", vendor_info)) { if (strbeg("RCD-1000", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; if (strbeg("RCD5020", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; if (strbeg("RCD5040", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; if (strbeg("RCD 4X4", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; } else if (strbeg("PIONEER", vendor_info)) { if (strbeg("CD-WO DW-S114X", prod_ident)) scgp->dev = DEV_PIONEER_DW_S114X; else if (strbeg("CD-WO DR-R504X", prod_ident)) /* Reoprt from philip@merge.com */ scgp->dev = DEV_PIONEER_DW_S114X; else if (strbeg("DVD-R DVR-S101", prod_ident)) scgp->dev = DEV_PIONEER_DVDR_S101; } else if (strbeg("PLASMON", vendor_info)) { if (strbeg("RF4100", prod_ident)) scgp->dev = DEV_PLASMON_RF_4100; else if (strbeg("CDR4220", prod_ident)) scgp->dev = DEV_CDD_2000; } else if (strbeg("PLEXTOR", vendor_info)) { if (strbeg("CD-R PX-R24CS", prod_ident)) scgp->dev = DEV_RICOH_RO_1420C; } else if (strbeg("RICOH", vendor_info)) { if (strbeg("RO-1420C", prod_ident)) scgp->dev = DEV_RICOH_RO_1420C; if (strbeg("RO1060C", prod_ident)) scgp->dev = DEV_RICOH_RO_1060C; } else if (strbeg("SAF", vendor_info)) { /* Smart & Friendly */ if (strbeg("CD-R2004", prod_ident) || strbeg("CD-R2006 ", prod_ident)) scgp->dev = DEV_SONY_CDU_924; else if (strbeg("CD-R2006PLUS", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; else if (strbeg("CD-RW226", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; else if (strbeg("CD-R4012", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; } else if (strbeg("SANYO", vendor_info)) { if (strbeg("CD-WO CRD-R24S", prod_ident)) scgp->dev = DEV_CDD_521; } else if (strbeg("SONY", vendor_info)) { if (strbeg("CD-R CDU92", prod_ident) || strbeg("CD-R CDU94", prod_ident)) scgp->dev = DEV_SONY_CDU_924; } else if (strbeg("TEAC", vendor_info)) { if (strbeg("CD-R50S", prod_ident) || strbeg("CD-R55S", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; } else if (strbeg("TRAXDATA", vendor_info) || strbeg("Traxdata", vendor_info)) { if (strbeg("CDR4120", prod_ident)) scgp->dev = DEV_TEAC_CD_R50S; } else if (strbeg("T.YUDEN", vendor_info)) { if (strbeg("CD-WO EW-50", prod_ident)) scgp->dev = DEV_TYUDEN_EW50; } else if (strbeg("WPI", vendor_info)) { /* Wearnes */ if (strbeg("CDR-632P", prod_ident)) scgp->dev = DEV_CDD_2600; } else if (strbeg("YAMAHA", vendor_info)) { if (strbeg("CDR10", prod_ident)) scgp->dev = DEV_YAMAHA_CDR_100; if (strbeg("CDR200", prod_ident)) scgp->dev = DEV_YAMAHA_CDR_400; if (strbeg("CDR400", prod_ident)) scgp->dev = DEV_YAMAHA_CDR_400; } else if (strbeg("MATSHITA", vendor_info)) { if (strbeg("CD-R CW-7501", prod_ident)) scgp->dev = DEV_MATSUSHITA_7501; if (strbeg("CD-R CW-7502", prod_ident)) scgp->dev = DEV_MATSUSHITA_7502; } if (scgp->dev == DEV_UNKNOWN) { /* * We do not have Manufacturer strings for * the following drives. */ if (strbeg("CDS615E", prod_ident)) /* Olympus */ scgp->dev = DEV_SONY_CDU_924; } if (scgp->dev == DEV_UNKNOWN && inq->type == INQ_ROMD) { BOOL cdrr = FALSE; BOOL cdwr = FALSE; BOOL cdrrw = FALSE; BOOL cdwrw = FALSE; BOOL dvd = FALSE; BOOL dvdwr = FALSE; scgp->dev = DEV_CDROM; if (mmc_check(scgp, &cdrr, &cdwr, &cdrrw, &cdwrw, &dvd, &dvdwr)) scgp->dev = DEV_MMC_CDROM; if (cdwr) scgp->dev = DEV_MMC_CDR; if (cdwrw) scgp->dev = DEV_MMC_CDRW; if (dvd) scgp->dev = DEV_MMC_DVD; if (dvdwr) scgp->dev = DEV_MMC_DVD_WR; } break; case INQ_PROCD: if (strbeg("BERTHOLD", vendor_info)) { if (strbeg("", prod_ident)) scgp->dev = DEV_HRSCAN; } break; case INQ_SCAN: scgp->dev = DEV_MS300A; break; } scgp->silent--; if (!print) return (TRUE); if (scgp->dev == DEV_UNKNOWN && !got_inquiry) { #ifdef PRINT_INQ_ERR scg_printerr(scgp); #endif return (FALSE); } printinq(scgp, stdout); return (TRUE); } EXPORT void printinq(scgp, f) SCSI *scgp; FILE *f; { register struct scsi_inquiry *inq = scgp->inq; fprintf(f, _("Device type : ")); scg_fprintdev(f, inq); fprintf(f, ("Version : %d\n"), inq->ansi_version); fprintf(f, _("Response Format: %d\n"), inq->data_format); if (inq->data_format >= 2) { fprintf(f, _("Capabilities : ")); if (inq->aenc) fprintf(f, "AENC "); if (inq->termiop) fprintf(f, "TERMIOP "); if (inq->reladr) fprintf(f, "RELADR "); if (inq->wbus32) fprintf(f, "WBUS32 "); if (inq->wbus16) fprintf(f, "WBUS16 "); if (inq->sync) fprintf(f, "SYNC "); if (inq->linked) fprintf(f, "LINKED "); if (inq->cmdque) fprintf(f, "CMDQUE "); if (inq->softreset) fprintf(f, "SOFTRESET "); fprintf(f, "\n"); } if (inq->add_len >= 31 || inq->inq_vendor_info[0] || inq->inq_prod_ident[0] || inq->inq_prod_revision[0]) { fprintf(f, _("Vendor_info : '%.8s'\n"), inq->inq_vendor_info); fprintf(f, _("Identifikation : '%.16s'\n"), inq->inq_prod_ident); fprintf(f, _("Revision : '%.4s'\n"), inq->inq_prod_revision); } } EXPORT void printdev(scgp) SCSI *scgp; { printf(_("Device seems to be: ")); switch (scgp->dev) { case DEV_UNKNOWN: printf(_("unknown")); break; case DEV_ACB40X0: printf("Adaptec 4000/4010/4070"); break; case DEV_ACB4000: printf("Adaptec 4000"); break; case DEV_ACB4010: printf("Adaptec 4010"); break; case DEV_ACB4070: printf("Adaptec 4070"); break; case DEV_ACB5500: printf("Adaptec 5500"); break; case DEV_ACB4520A: printf("Adaptec 4520A"); break; case DEV_ACB4525: printf("Adaptec 4525"); break; case DEV_MD21: printf("Emulex MD21"); break; case DEV_MD23: printf("Emulex MD23"); break; case DEV_NON_CCS_DSK: printf("Generic NON CCS Disk"); break; case DEV_CCS_GENDISK: printf("Generic CCS Disk"); break; case DEV_SONY_SMO: printf("Sony SMO-C501"); break; case DEV_MT02: printf("Emulex MT02"); break; case DEV_SC4000: printf("Sysgen SC4000"); break; case DEV_RXT800S: printf("Maxtor RXT800S"); break; case DEV_HRSCAN: printf("Berthold HR-Scanner"); break; case DEV_MS300A: printf("Microtek MS300A"); break; case DEV_CDROM: printf("Generic CD-ROM"); break; case DEV_MMC_CDROM: printf("Generic mmc CD-ROM"); break; case DEV_MMC_CDR: printf("Generic mmc CD-R"); break; case DEV_MMC_CDRW: printf("Generic mmc CD-RW"); break; case DEV_MMC_DVD: printf("Generic mmc2 DVD-ROM"); break; case DEV_MMC_DVD_WR: printf("Generic mmc2 DVD-R/DVD-RW/DVD-RAM"); break; case DEV_CDD_521_OLD: printf("Philips old CDD-521"); break; case DEV_CDD_521: printf("Philips CDD-521"); break; case DEV_CDD_522: printf("Philips CDD-522"); break; case DEV_PCD_600: printf("Kodak PCD-600"); break; case DEV_CDD_2000: printf("Philips CDD-2000"); break; case DEV_CDD_2600: printf("Philips CDD-2600"); break; case DEV_YAMAHA_CDR_100:printf("Yamaha CDR-100"); break; case DEV_YAMAHA_CDR_400:printf("Yamaha CDR-400"); break; case DEV_PLASMON_RF_4100:printf("Plasmon RF-4100"); break; case DEV_SONY_CDU_924: printf("Sony CDU-924S"); break; case DEV_RICOH_RO_1060C:printf("Ricoh RO-1060C"); break; case DEV_RICOH_RO_1420C:printf("Ricoh RO-1420C"); break; case DEV_TEAC_CD_R50S: printf("Teac CD-R50S"); break; case DEV_MATSUSHITA_7501:printf("Matsushita CW-7501"); break; case DEV_MATSUSHITA_7502:printf("Matsushita CW-7502"); break; case DEV_PIONEER_DW_S114X: printf("Pioneer DW-S114X"); break; case DEV_PIONEER_DVDR_S101:printf("Pioneer DVD-R S101"); break; default: printf(_("Missing Entry for dev %d"), scgp->dev); break; } printf(".\n"); } EXPORT BOOL do_inquiry(scgp, print) SCSI *scgp; int print; { if (getdev(scgp, print)) { if (print) printdev(scgp); return (TRUE); } else { return (FALSE); } } EXPORT BOOL recovery_needed(scgp, dp) SCSI *scgp; cdr_t *dp; { int err; register struct scg_cmd *scmd = scgp->scmd; scgp->silent++; err = test_unit_ready(scgp); scgp->silent--; if (err >= 0) return (FALSE); else if (scmd->error >= SCG_FATAL) /* nicht selektierbar */ return (FALSE); if (scmd->sense.code < 0x70) /* non extended Sense */ return (FALSE); /* XXX Old Philips code */ return (((struct scsi_ext_sense *)&scmd->sense)->sense_code == 0xD0); } EXPORT int scsi_load(scgp, dp) SCSI *scgp; cdr_t *dp; { int key; int code; if (dp && (dp->cdr_flags & CDR_CADDYLOAD) == 0) { if (scsi_start_stop_unit(scgp, 1, 1, dp->cdr_cmdflags&F_IMMED) >= 0) return (0); } if (wait_unit_ready(scgp, 60)) return (0); key = scg_sense_key(scgp); code = scg_sense_code(scgp); if (key == SC_NOT_READY && (code == 0x3A || code == 0x30)) { errmsgno(EX_BAD, _("Cannot load media with %s drive!\n"), dp && (dp->cdr_flags & CDR_CADDYLOAD) ? _("caddy") : _("this")); errmsgno(EX_BAD, _("Try to load media by hand.\n")); } return (-1); } EXPORT int scsi_unload(scgp, dp) SCSI *scgp; cdr_t *dp; { return (scsi_start_stop_unit(scgp, 0, 1, dp && (dp->cdr_cmdflags&F_IMMED))); } EXPORT int scsi_cdr_write(scgp, bp, sectaddr, size, blocks, islast) SCSI *scgp; caddr_t bp; /* address of buffer */ long sectaddr; /* disk address (sector) to put */ long size; /* number of bytes to transfer */ int blocks; /* sector count */ BOOL islast; /* last write for track */ { return (write_xg1(scgp, bp, sectaddr, size, blocks)); } EXPORT struct cd_mode_page_2A * mmc_cap(scgp, modep) SCSI *scgp; Uchar *modep; { int len; int val; Uchar mode[0x100]; struct cd_mode_page_2A *mp; struct cd_mode_page_2A *mp2; retry: fillbytes((caddr_t)mode, sizeof (mode), '\0'); if (!get_mode_params(scgp, 0x2A, _("CD capabilities"), mode, (Uchar *)0, (Uchar *)0, (Uchar *)0, &len)) { if (scg_sense_key(scgp) == SC_NOT_READY) { if (wait_unit_ready(scgp, 60)) goto retry; } return (NULL); /* Pre SCSI-3/mmc drive */ } if (len == 0) /* Pre SCSI-3/mmc drive */ return (NULL); mp = (struct cd_mode_page_2A *) (mode + sizeof (struct scsi_mode_header) + ((struct scsi_mode_header *)mode)->blockdesc_len); /* * Do some heuristics against pre SCSI-3/mmc VU page 2A * We should test for a minimum p_len of 0x14, but some * buggy CD-ROM readers ommit the write speed values. */ if (mp->p_len < 0x10) return (NULL); val = a_to_u_2_byte(mp->max_read_speed); if (val != 0 && val < 176) return (NULL); val = a_to_u_2_byte(mp->cur_read_speed); if (val != 0 && val < 176) return (NULL); len -= sizeof (struct scsi_mode_header) + ((struct scsi_mode_header *)mode)->blockdesc_len; if (modep) mp2 = (struct cd_mode_page_2A *)modep; else mp2 = malloc(len); if (mp2) movebytes(mp, mp2, len); return (mp2); } EXPORT void mmc_getval(mp, cdrrp, cdwrp, cdrrwp, cdwrwp, dvdp, dvdwp) struct cd_mode_page_2A *mp; BOOL *cdrrp; /* CD ROM */ BOOL *cdwrp; /* CD-R writer */ BOOL *cdrrwp; /* CD-RW reader */ BOOL *cdwrwp; /* CD-RW writer */ BOOL *dvdp; /* DVD reader */ BOOL *dvdwp; /* DVD writer */ { BOOL isdvd; /* Any DVD reader */ BOOL isdvd_wr; /* DVD writer (R / RAM) */ BOOL iscd_wr; /* CD writer */ iscd_wr = (mp->cd_r_write != 0) || /* SCSI-3/mmc CD-R */ (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */ if (cdrrp) *cdrrp = (mp->cd_r_read != 0); /* SCSI-3/mmc CD */ if (cdwrp) *cdwrp = (mp->cd_r_write != 0); /* SCSI-3/mmc CD-R */ if (cdrrwp) *cdrrwp = (mp->cd_rw_read != 0); /* SCSI-3/mmc CD */ if (cdwrwp) *cdwrwp = (mp->cd_rw_write != 0); /* SCSI-3/mmc CD-RW */ isdvd = /* SCSI-3/mmc2 DVD */ (mp->dvd_ram_read + mp->dvd_r_read + mp->dvd_rom_read) != 0; isdvd_wr = /* SCSI-3/mmc2 DVD writer*/ (mp->dvd_ram_write + mp->dvd_r_write) != 0; if (dvdp) *dvdp = isdvd; if (dvdwp) *dvdwp = isdvd_wr; } EXPORT BOOL is_mmc(scgp, cdwp, dvdwp) SCSI *scgp; BOOL *cdwp; /* CD writer */ BOOL *dvdwp; /* DVD writer */ { BOOL cdwr = FALSE; BOOL cdwrw = FALSE; if (cdwp) *cdwp = FALSE; if (dvdwp) *dvdwp = FALSE; if (!mmc_check(scgp, NULL, &cdwr, NULL, &cdwrw, NULL, dvdwp)) return (FALSE); if (cdwp) *cdwp = cdwr | cdwrw; return (TRUE); } EXPORT BOOL mmc_check(scgp, cdrrp, cdwrp, cdrrwp, cdwrwp, dvdp, dvdwp) SCSI *scgp; BOOL *cdrrp; /* CD ROM */ BOOL *cdwrp; /* CD-R writer */ BOOL *cdrrwp; /* CD-RW reader */ BOOL *cdwrwp; /* CD-RW writer */ BOOL *dvdp; /* DVD reader */ BOOL *dvdwp; /* DVD writer */ { Uchar mode[0x100]; BOOL was_atapi; struct cd_mode_page_2A *mp; if (scgp->inq->type != INQ_ROMD) return (FALSE); fillbytes((caddr_t)mode, sizeof (mode), '\0'); was_atapi = allow_atapi(scgp, TRUE); scgp->silent++; mp = mmc_cap(scgp, mode); scgp->silent--; allow_atapi(scgp, was_atapi); if (mp == NULL) return (FALSE); mmc_getval(mp, cdrrp, cdwrp, cdrrwp, cdwrwp, dvdp, dvdwp); return (TRUE); /* Generic SCSI-3/mmc CD */ } LOCAL void print_speed(fmt, val) char *fmt; int val; { printf(" %s: %5d kB/s", fmt, val); printf(" (CD %3ux,", val/176); printf(" DVD %2ux,", val/1385); printf(" BD %2ux)\n", val/4495); } #define DOES(what, flag) printf(_(" Does %s%s\n"), flag?"":_("not "), what) #define IS(what, flag) printf(_(" Is %s%s\n"), flag?"":_("not "), what) #define VAL(what, val) printf(_(" %s: %d\n"), what, val[0]*256 + val[1]) #define SVAL(what, val) printf(_(" %s: %s\n"), what, val) EXPORT void print_capabilities(scgp) SCSI *scgp; { BOOL was_atapi; Uchar mode[0x100]; struct cd_mode_page_2A *mp; static const char *bclk[4] = {"32", "16", "24", "24 (I2S)"}; static const char *load[8] = {"caddy", "tray", "pop-up", "reserved(3)", "disc changer", "cartridge changer", "reserved(6)", "reserved(7)" }; static const char *rotctl[4] = {"CLV/PCAV", "CAV", "reserved(2)", "reserved(3)"}; if (scgp->inq->type != INQ_ROMD) return; fillbytes((caddr_t)mode, sizeof (mode), '\0'); was_atapi = allow_atapi(scgp, TRUE); /* Try to switch to 10 byte mode cmds */ scgp->silent++; mp = mmc_cap(scgp, mode); scgp->silent--; allow_atapi(scgp, was_atapi); if (mp == NULL) return; printf(_("\nDrive capabilities, per")); if (mp->p_len >= 28) printf(" MMC-3"); else if (mp->p_len >= 24) printf(" MMC-2"); else printf(" MMC"); printf(_(" page 2A:\n\n")); DOES(_("read CD-R media"), mp->cd_r_read); DOES(_("write CD-R media"), mp->cd_r_write); DOES(_("read CD-RW media"), mp->cd_rw_read); DOES(_("write CD-RW media"), mp->cd_rw_write); DOES(_("read DVD-ROM media"), mp->dvd_rom_read); DOES(_("read DVD-R media"), mp->dvd_r_read); DOES(_("write DVD-R media"), mp->dvd_r_write); DOES(_("read DVD-RAM media"), mp->dvd_ram_read); DOES(_("write DVD-RAM media"), mp->dvd_ram_write); DOES(_("support test writing"), mp->test_write); printf("\n"); DOES(_("read Mode 2 Form 1 blocks"), mp->mode_2_form_1); DOES(_("read Mode 2 Form 2 blocks"), mp->mode_2_form_2); DOES(_("read digital audio blocks"), mp->cd_da_supported); if (mp->cd_da_supported) DOES(_("restart non-streamed digital audio reads accurately"), mp->cd_da_accurate); DOES(_("support Buffer-Underrun-Free recording"), mp->BUF); DOES(_("read multi-session CDs"), mp->multi_session); DOES(_("read fixed-packet CD media using Method 2"), mp->method2); DOES(_("read CD bar code"), mp->read_bar_code); DOES(_("read R-W subcode information"), mp->rw_supported); if (mp->rw_supported) DOES(_("return R-W subcode de-interleaved and error-corrected"), mp->rw_deint_corr); DOES(_("read raw P-W subcode data from lead in"), mp->pw_in_lead_in); DOES(_("return CD media catalog number"), mp->UPC); DOES(_("return CD ISRC information"), mp->ISRC); DOES(_("support C2 error pointers"), mp->c2_pointers); DOES(_("deliver composite A/V data"), mp->composite); printf("\n"); DOES(_("play audio CDs"), mp->audio_play); if (mp->audio_play) { VAL(_("Number of volume control levels"), mp->num_vol_levels); DOES(_("support individual volume control setting for each channel"), mp->sep_chan_vol); DOES(_("support independent mute setting for each channel"), mp->sep_chan_mute); DOES(_("support digital output on port 1"), mp->digital_port_1); DOES(_("support digital output on port 2"), mp->digital_port_2); if (mp->digital_port_1 || mp->digital_port_2) { DOES(_("send digital data LSB-first"), mp->LSBF); DOES(_("set LRCK high for left-channel data"), mp->RCK); DOES(_("have valid data on falling edge of clock"), mp->BCK); SVAL(_("Length of data in BCLKs"), bclk[mp->length]); } } printf("\n"); SVAL(_("Loading mechanism type"), load[mp->loading_type]); DOES(_("support ejection of CD via START/STOP command"), mp->eject); DOES(_("lock media on power up via prevent jumper"), mp->prevent_jumper); DOES(_("allow media to be locked in the drive via PREVENT/ALLOW command"), mp->lock); IS(_("currently in a media-locked state"), mp->lock_state); DOES(_("support changing side of disk"), mp->side_change); DOES(_("have load-empty-slot-in-changer feature"), mp->sw_slot_sel); DOES(_("support Individual Disk Present feature"), mp->disk_present_rep); printf("\n"); print_speed(_("Maximum read speed"), a_to_u_2_byte(mp->max_read_speed)); print_speed(_("Current read speed"), a_to_u_2_byte(mp->cur_read_speed)); print_speed(_("Maximum write speed"), a_to_u_2_byte(mp->max_write_speed)); if (mp->p_len >= 28) print_speed(_("Current write speed"), a_to_u_2_byte(mp->v3_cur_write_speed)); else print_speed(_("Current write speed"), a_to_u_2_byte(mp->cur_write_speed)); if (mp->p_len >= 28) { SVAL(_("Rotational control selected"), rotctl[mp->rot_ctl_sel]); } VAL(_("Buffer size in KB"), mp->buffer_size); if (mp->p_len >= 24) { VAL(_("Copy management revision supported"), mp->copy_man_rev); } if (mp->p_len >= 28) { struct cd_wr_speed_performance *pp; Uint ndesc; Uint i; Uint n; ndesc = a_to_u_2_byte(mp->num_wr_speed_des); pp = mp->wr_speed_des; printf(_(" Number of supported write speeds: %d\n"), ndesc); for (i = 0; i < ndesc; i++, pp++) { printf(_(" Write speed # %d:"), i); n = a_to_u_2_byte(pp->wr_speed_supp); printf(" %5d kB/s", n); printf(" %s", rotctl[pp->rot_ctl_sel]); printf(" (CD %3ux,", n/176); printf(" DVD %2ux,", n/1385); printf(" BD %2ux)\n", n/4495); } } /* Generic SCSI-3/mmc CD */ } EXPORT int verify(scgp, start, count, bad_block) SCSI *scgp; long start; int count; long *bad_block; { register struct scg_cmd *scmd = scgp->scmd; fillbytes((caddr_t)scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t)0; scmd->size = 0; scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G1_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g1_cdb.cmd = 0x2F; /* Verify */ scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdbaddr(&scmd->cdb.g1_cdb, start); g1_cdblen(&scmd->cdb.g1_cdb, count); scgp->cmdname = "verify"; if (scg_cmd(scgp) < 0) { if (scmd->sense.code >= 0x70) { /* extended Sense */ *bad_block = a_to_4_byte(&((struct scsi_ext_sense *) &scmd->sense)->info_1); } else { *bad_block = a_to_u_3_byte(&scmd->sense.high_addr); } return (-1); } return (0); }