/* @(#)scsi_mmc.c 1.51 10/12/19 Copyright 2002-2010 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)scsi_mmc.c 1.51 10/12/19 Copyright 2002-2010 J. Schilling"; #endif /* * SCSI command functions for cdrecord * covering MMC-3 level and above * * Copyright (c) 2002-2010 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. */ /*#define DEBUG*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "scsimmc.h" #include "cdrecord.h" extern int xdebug; extern int lverbose; LOCAL struct features { UInt16_t code; char *name; } fl[] = { { 0x0000, "Profile List", }, { 0x0001, "Core", }, { 0x0002, "Morphing", }, { 0x0003, "Removable Medium", }, { 0x0004, "Write Protect", }, { 0x0010, "Random Readable", }, { 0x001D, "Multi Read", }, { 0x001E, "CD Read", }, { 0x001F, "DVD Read", }, { 0x0020, "Random Writable", }, { 0x0021, "Incremental Streaming Writable", }, { 0x0022, "Sector Erasable", }, { 0x0023, "Formattable", }, { 0x0024, "Defect Management", }, { 0x0025, "Write Once", }, { 0x0026, "Restricted Overwrite", }, { 0x0027, "CD-RW CAV Write", }, { 0x0028, "MRW", }, { 0x0029, "Ehanced Defect Reporting", }, { 0x002A, "DVD+RW", }, { 0x002B, "DVD+R", }, { 0x002C, "Rigid Restricted Overwrite", }, { 0x002D, "CD Track at Once", }, { 0x002E, "CD Mastering", }, { 0x002F, "DVD-R/-RW Write", }, { 0x0030, "DDCD Read", }, { 0x0031, "DDCD-R Write", }, { 0x0032, "DDCD-RW Write", }, { 0x0033, "Layer Jump Recording", }, { 0x0037, "CD-RW Write", }, { 0x0038, "BD-R Pseudo-Overwrite (POW)", }, { 0x003A, "DVD+RW/DL Read", }, { 0x003B, "DVD+R/DL Read", }, { 0x0040, "BD Read", }, { 0x0041, "BD Write", }, { 0x0042, "Time Safe Recording (TSR)", }, { 0x0050, "HD-DVD Read", }, { 0x0051, "HD-DVD Write", }, { 0x0080, "Hybrid Disk Read", }, { 0x0100, "Power Management", }, { 0x0101, "S.M.A.R.T.", }, { 0x0102, "Embedded Changer", }, { 0x0103, "CD Audio analog play", }, { 0x0104, "Microcode Upgrade", }, { 0x0105, "Time-out", }, { 0x0106, "DVD-CSS", }, { 0x0107, "Real Time Streaming", }, { 0x0108, "Logical Unit Serial Number", }, { 0x0109, "Media Serial Number", }, { 0x010A, "Disk Control Blocks", }, { 0x010B, "DVD CPRM", }, { 0x010C, "Microcode Information", }, { 0x010D, "AACS", }, { 0x0110, "VCPS", }, }; LOCAL struct profiles { UInt16_t code; char *name; } pl[] = { { 0x0000, "Reserved", }, { 0x0001, "Non -removable Disk", }, { 0x0002, "Removable Disk", }, { 0x0003, "MO Erasable", }, { 0x0004, "MO Write Once", }, { 0x0005, "AS-MO", }, /* 0x06..0x07 is reserved */ { 0x0008, "CD-ROM", }, { 0x0009, "CD-R", }, { 0x000A, "CD-RW", }, /* 0x0B..0x0F is reserved */ { 0x0010, "DVD-ROM", }, { 0x0011, "DVD-R sequential recording", }, { 0x0012, "DVD-RAM", }, { 0x0013, "DVD-RW restricted overwrite", }, { 0x0014, "DVD-RW sequential recording", }, { 0x0015, "DVD-R/DL sequential recording", }, { 0x0016, "DVD-R/DL layer jump recording", }, { 0x0017, "DVD-RW/DL", }, /* 0x18..0x19 is reserved */ { 0x001A, "DVD+RW", }, { 0x001B, "DVD+R", }, { 0x0020, "DDCD-ROM", }, { 0x0021, "DDCD-R", }, { 0x0022, "DDCD-RW", }, { 0x002A, "DVD+RW/DL", }, { 0x002B, "DVD+R/DL", }, { 0x0040, "BD-ROM", }, { 0x0041, "BD-R sequential recording", }, { 0x0042, "BD-R random recording", }, { 0x0043, "BD-RE", }, /* 0x44..0x4F is reserved */ { 0x0050, "HD DVD-ROM", }, { 0x0051, "HD DVD-R", }, { 0x0052, "HD DVD-RAM", }, { 0x0053, "HD DVD-RW", }, /* 0x54..0x57 is reserved */ { 0x0058, "HD DVD-R/DL", }, { 0x005A, "HD DVD-RW/DL", }, { 0xFFFF, "No standard Profile", }, }; EXPORT int get_configuration __PR((SCSI *scgp, caddr_t bp, int cnt, int st_feature, int rt)); LOCAL int get_conflen __PR((SCSI *scgp, int st_feature, int rt)); EXPORT int get_curprofile __PR((SCSI *scgp)); LOCAL int get_profiles __PR((SCSI *scgp, caddr_t bp, int cnt)); EXPORT int has_profile __PR((SCSI *scgp, int profile)); EXPORT int print_profiles __PR((SCSI *scgp)); EXPORT int get_proflist __PR((SCSI *scgp, BOOL *wp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp, BOOL *ddcdp)); EXPORT int get_wproflist __PR((SCSI *scgp, BOOL *cdp, BOOL *dvdp, BOOL *dvdplusp, BOOL *ddcdp)); EXPORT int get_mediatype __PR((SCSI *scgp)); EXPORT int get_singlespeed __PR((int mt)); EXPORT float get_secsps __PR((int mt)); EXPORT char *get_mclassname __PR((int mt)); EXPORT int get_blf __PR((int mt)); LOCAL int scsi_get_performance __PR((SCSI *scgp, caddr_t bp, int cnd, int ndesc, int type, int datatype)); EXPORT int scsi_get_perf_maxspeed __PR((SCSI *scgp, Ulong *readp, Ulong *writep, Ulong *endp)); EXPORT int scsi_get_perf_curspeed __PR((SCSI *scgp, Ulong *readp, Ulong *writep, Ulong *endp)); LOCAL int scsi_set_streaming __PR((SCSI *scgp, Ulong *readp, Ulong *writep, Ulong *endp)); EXPORT int speed_select_mdvd __PR((SCSI *scgp, int readspeed, int writespeed)); LOCAL char *fname __PR((Uint code)); LOCAL char *pname __PR((Uint code)); LOCAL BOOL fname_known __PR((Uint code)); LOCAL BOOL pname_known __PR((Uint code)); EXPORT int print_features __PR((SCSI *scgp)); EXPORT void print_format_capacities __PR((SCSI *scgp)); EXPORT int get_format_capacities __PR((SCSI *scgp, caddr_t bp, int cnt)); EXPORT int read_format_capacities __PR((SCSI *scgp, caddr_t bp, int cnt)); EXPORT void przone __PR((struct rzone_info *rp)); EXPORT int get_diskinfo __PR((SCSI *scgp, struct disk_info *dip, int cnt)); EXPORT char *get_ses_type __PR((int ses_type)); EXPORT void print_diskinfo __PR((struct disk_info *dip, BOOL is_cd)); EXPORT int prdiskstatus __PR((SCSI *scgp, cdr_t *dp, BOOL is_cd)); EXPORT int sessstatus __PR((SCSI *scgp, BOOL is_cd, long *offp, long *nwap)); EXPORT void print_performance_mmc __PR((SCSI *scgp)); /* * Get feature codes */ EXPORT int get_configuration(scgp, bp, cnt, st_feature, rt) SCSI *scgp; caddr_t bp; int cnt; int st_feature; int rt; { 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 = 0x46; scmd->cdb.g1_cdb.lun = scg_lun(scgp); if (rt & 1) scmd->cdb.g1_cdb.reladr = 1; if (rt & 2) scmd->cdb.g1_cdb.res = 1; i_to_2_byte(scmd->cdb.g1_cdb.addr, st_feature); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "get_configuration"; return (scg_cmd(scgp)); } /* * Retrieve feature code list length */ LOCAL int get_conflen(scgp, st_feature, rt) SCSI *scgp; int st_feature; int rt; { Uchar cbuf[8]; int flen; int i; fillbytes(cbuf, sizeof (cbuf), '\0'); scgp->silent++; i = get_configuration(scgp, (char *)cbuf, sizeof (cbuf), st_feature, rt); scgp->silent--; if (i < 0) return (-1); i = sizeof (cbuf) - scg_getresid(scgp); if (i < 4) return (-1); flen = a_to_u_4_byte(cbuf); if (flen < 4) return (-1); return (flen); } EXPORT int get_curprofile(scgp) SCSI *scgp; { Uchar cbuf[8]; int amt; int flen; int profile; int i; fillbytes(cbuf, sizeof (cbuf), '\0'); scgp->silent++; i = get_configuration(scgp, (char *)cbuf, sizeof (cbuf), 0, 0); scgp->silent--; if (i < 0) return (-1); amt = sizeof (cbuf) - scg_getresid(scgp); if (amt < 8) return (-1); flen = a_to_u_4_byte(cbuf); if (flen < 4) return (-1); profile = a_to_u_2_byte(&cbuf[6]); if (xdebug > 1) scg_prbytes(_("Features: "), cbuf, amt); if (xdebug > 0) printf(_("feature len: %d current profile 0x%04X len %d\n"), flen, profile, amt); return (profile); } LOCAL int get_profiles(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { int amt; int flen; int i; flen = get_conflen(scgp, 0, 0); if (flen < 0) return (-1); if (cnt < flen) flen = cnt; fillbytes(bp, cnt, '\0'); scgp->silent++; i = get_configuration(scgp, (char *)bp, flen, 0, 0); scgp->silent--; if (i < 0) return (-1); amt = flen - scg_getresid(scgp); flen = a_to_u_4_byte(bp); if ((flen+4) < amt) amt = flen+4; return (amt); } EXPORT int has_profile(scgp, profile) SCSI *scgp; int profile; { Uchar cbuf[1024]; Uchar *p; int flen; int prf; int i; int n; flen = get_profiles(scgp, (caddr_t)cbuf, sizeof (cbuf)); if (flen < 0) return (-1); p = cbuf; p += 8; /* Skip feature header */ n = p[3]; /* Additional length */ n /= 4; p += 4; for (i = 0; i < n; i++) { prf = a_to_u_2_byte(p); if (xdebug > 0) printf(_("Profile: 0x%04X "), prf); if (profile == prf) return (1); p += 4; } return (0); } EXPORT int print_profiles(scgp) SCSI *scgp; { Uchar cbuf[1024]; Uchar *p; int flen; int curprofile; int profile; int i; int n; flen = get_profiles(scgp, (caddr_t)cbuf, sizeof (cbuf)); if (flen < 0) return (-1); p = cbuf; if (xdebug > 1) scg_prbytes(_("Features: "), cbuf, flen); curprofile = a_to_u_2_byte(&p[6]); if (xdebug > 0) printf(_("feature len: %d current profile 0x%04X\n"), flen, curprofile); if (pname_known(curprofile)) printf(_("Current: %s\n"), curprofile == 0 ? _("none") : pname(curprofile)); else printf(_("Current: 0x%04X unknown\n"), curprofile); p += 8; /* Skip feature header */ n = p[3]; /* Additional length */ n /= 4; p += 4; for (i = 0; i < n; i++) { profile = a_to_u_2_byte(p); if (xdebug > 0) printf(_("Profile: 0x%04X "), profile); else printf(_("Profile: ")); if (pname_known(profile)) printf("%s %s\n", pname(profile), p[2] & 1 ? _("(current)"):""); else printf("0x%04X %s\n", profile, p[2] & 1 ? _("(current)"):""); p += 4; } return (curprofile); } EXPORT int get_proflist(scgp, wp, cdp, dvdp, dvdplusp, ddcdp) SCSI *scgp; BOOL *wp; BOOL *cdp; BOOL *dvdp; BOOL *dvdplusp; BOOL *ddcdp; { Uchar cbuf[1024]; Uchar *p; int flen; int curprofile; int profile; int i; int n; BOOL wr = FALSE; BOOL cd = FALSE; BOOL dvd = FALSE; BOOL dvdplus = FALSE; BOOL ddcd = FALSE; flen = get_profiles(scgp, (caddr_t)cbuf, sizeof (cbuf)); if (flen < 0) return (-1); p = cbuf; if (xdebug > 1) scg_prbytes(_("Features: "), cbuf, flen); curprofile = a_to_u_2_byte(&p[6]); if (xdebug > 0) printf(_("feature len: %d current profile 0x%04X\n"), flen, curprofile); p += 8; /* Skip feature header */ n = p[3]; /* Additional length */ n /= 4; p += 4; for (i = 0; i < n; i++) { profile = a_to_u_2_byte(p); p += 4; if (profile >= 0x0008 && profile < 0x0010) cd = TRUE; if (profile > 0x0008 && profile < 0x0010) wr = TRUE; if (profile >= 0x0010 && profile < 0x0018) dvd = TRUE; if (profile > 0x0010 && profile < 0x0018) wr = TRUE; if (profile >= 0x0018 && profile < 0x0020) dvdplus = TRUE; if (profile > 0x0018 && profile < 0x0020) wr = TRUE; if (profile >= 0x0020 && profile < 0x0028) ddcd = TRUE; if (profile > 0x0020 && profile < 0x0028) wr = TRUE; } if (wp) *wp = wr; if (cdp) *cdp = cd; if (dvdp) *dvdp = dvd; if (dvdplusp) *dvdplusp = dvdplus; if (ddcdp) *ddcdp = ddcd; return (curprofile); } EXPORT int get_wproflist(scgp, cdp, dvdp, dvdplusp, ddcdp) SCSI *scgp; BOOL *cdp; BOOL *dvdp; BOOL *dvdplusp; BOOL *ddcdp; { Uchar cbuf[1024]; Uchar *p; int flen; int curprofile; int profile; int i; int n; BOOL cd = FALSE; BOOL dvd = FALSE; BOOL dvdplus = FALSE; BOOL ddcd = FALSE; flen = get_profiles(scgp, (caddr_t)cbuf, sizeof (cbuf)); if (flen < 0) return (-1); p = cbuf; curprofile = a_to_u_2_byte(&p[6]); p += 8; /* Skip feature header */ n = p[3]; /* Additional length */ n /= 4; p += 4; for (i = 0; i < n; i++) { profile = a_to_u_2_byte(p); p += 4; if (profile > 0x0008 && profile < 0x0010) cd = TRUE; if (profile > 0x0010 && profile < 0x0018) dvd = TRUE; if (profile > 0x0018 && profile < 0x0020) dvdplus = TRUE; if (profile > 0x0020 && profile < 0x0028) ddcd = TRUE; } if (cdp) *cdp = cd; if (dvdp) *dvdp = dvd; if (dvdplusp) *dvdplusp = dvdplus; if (ddcdp) *ddcdp = ddcd; return (curprofile); } EXPORT int get_mediatype(scgp) SCSI *scgp; { int profile = get_curprofile(scgp); if (profile < 0x08) return (MT_NONE); if (profile >= 0x08 && profile < 0x10) return (MT_CD); if (profile >= 0x10 && profile < 0x40) return (MT_DVD); if (profile >= 0x40 && profile < 0x50) return (MT_BD); if (profile >= 0x50 && profile < 0x60) return (MT_HDDVD); return (MT_NONE); } EXPORT int get_singlespeed(mt) int mt; { switch (mt) { case MT_CD: return (176); case MT_DVD: return (1385); case MT_BD: return (4495); case MT_HDDVD: return (4495); /* XXX ??? */ case MT_NONE: default: return (1); } } EXPORT float get_secsps(mt) int mt; { switch (mt) { case MT_CD: return ((float)75.0); case MT_DVD: return ((float)676.27); case MT_BD: return ((float)2195.07); case MT_HDDVD: return ((float)2195.07); /* XXX ??? */ case MT_NONE: default: return ((float)75.0); } } EXPORT char * get_mclassname(mt) int mt; { switch (mt) { case MT_CD: return ("CD"); case MT_DVD: return ("DVD"); case MT_BD: return ("BD"); case MT_HDDVD: return ("HD-DVD"); case MT_NONE: default: return ("NONE"); } } /* * Guessed blocking factor based on media type */ EXPORT int get_blf(mt) int mt; { switch (mt) { case MT_DVD: return (16); case MT_BD: return (32); case MT_HDDVD: return (32); /* XXX ??? */ default: return (1); } } LOCAL int scsi_get_performance(scgp, bp, cnt, ndesc, type, datatype) SCSI *scgp; caddr_t bp; int cnt; int ndesc; int type; int datatype; { 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->cdb.g1_cdb.cmd = 0xAC; scmd->cdb.g1_cdb.lun = scg_lun(scgp); scmd->cdb.cmd_cdb[1] |= datatype & 0x1F; scmd->cdb.cmd_cdb[9] = ndesc; scmd->cdb.cmd_cdb[10] = type; scgp->cmdname = "get performance"; return (scg_cmd(scgp)); } EXPORT int scsi_get_perf_maxspeed(scgp, readp, writep, endp) SCSI *scgp; Ulong *readp; Ulong *writep; Ulong *endp; { register struct scg_cmd *scmd = scgp->scmd; struct mmc_performance_header *ph; struct mmc_write_speed *wsp; #define MAX_AMT 100 char buffer[8 + MAX_AMT*16]; Ulong ul; int amt; int i; int mt = 0; int ssp = 1; char *mname = NULL; if (xdebug != 0) { mt = get_mediatype(scgp); ssp = get_singlespeed(mt); mname = get_mclassname(mt); } fillbytes((caddr_t) buffer, sizeof (buffer), '\0'); ph = (struct mmc_performance_header *)buffer; if (scsi_get_performance(scgp, buffer, 8+16, 1, 0x03, 0) < 0) return (-1); amt = (a_to_4_byte(ph->p_datalen) -4)/sizeof (struct mmc_write_speed); if (amt < 1) amt = 1; if (amt > MAX_AMT) amt = MAX_AMT; if (scsi_get_performance(scgp, buffer, 8+amt*16, amt, 0x03, 0) < 0) return (-1); #ifdef XDEBUG error(_("Bytes: %d\n"), scmd->size - scg_getresid(scgp)); error(_("header: %ld\n"), a_to_4_byte(buffer) + 4); #endif ph = (struct mmc_performance_header *)buffer; wsp = (struct mmc_write_speed *)(((char *)ph) + sizeof (struct mmc_performance_header)); ul = a_to_u_4_byte(wsp->end_lba); if (endp) *endp = ul; ul = a_to_u_4_byte(wsp->read_speed); if (readp) *readp = ul; ul = a_to_u_4_byte(wsp->write_speed); if (writep) *writep = ul; wsp = (struct mmc_write_speed *)(((char *)ph) + sizeof (struct mmc_performance_header)); i = (a_to_4_byte(buffer) -4)/sizeof (struct mmc_write_speed); if (i > scmd->cdb.cmd_cdb[9]) i = scmd->cdb.cmd_cdb[9]; if (xdebug > 0) error(_("MaxSpeed Nperf: %d\n"), i); if (xdebug != 0) for (; --i >= 0; wsp++) { ul = a_to_u_4_byte(wsp->end_lba); error(_("End LBA: %7lu\n"), ul); ul = a_to_u_4_byte(wsp->read_speed); error(_("Read Speed: %7lu == %lux %s\n"), ul, ul/ssp, mname); ul = a_to_u_4_byte(wsp->write_speed); error(_("Write Speed: %7lu == %lux %s\n"), ul, ul/ssp, mname); error("\n"); } #ifdef XDEBUG scg_prbytes(_("Performance data:"), (Uchar *)buffer, scmd->size - scg_getresid(scgp)); #endif return (0); #undef MAX_AMT } EXPORT int scsi_get_perf_curspeed(scgp, readp, writep, endp) SCSI *scgp; Ulong *readp; Ulong *writep; Ulong *endp; { register struct scg_cmd *scmd = scgp->scmd; struct mmc_performance_header *ph; struct mmc_performance *perfp; #define MAX_AMT 100 char buffer[8 + MAX_AMT*16]; Ulong ul; Ulong end; Ulong speed; int amt; int i; int mt = 0; int ssp = 1; char *mname = NULL; if (xdebug != 0) { mt = get_mediatype(scgp); ssp = get_singlespeed(mt); mname = get_mclassname(mt); } if (endp || writep) { fillbytes((caddr_t) buffer, sizeof (buffer), '\0'); scgp->silent++; if (scsi_get_performance(scgp, buffer, 8+16, 1, 0x00, 0x04) < 0) { scgp->silent--; goto doread; } scgp->silent--; ph = (struct mmc_performance_header *)buffer; amt = (a_to_4_byte(ph->p_datalen) -4)/sizeof (struct mmc_performance); if (amt < 1) amt = 1; if (amt > MAX_AMT) amt = MAX_AMT; if (scsi_get_performance(scgp, buffer, 8+16*amt, amt, 0x00, 0x04) < 0) return (-1); #ifdef XDEBUG error(_("Bytes: %d\n"), scmd->size - scg_getresid(scgp)); error(_("header: %ld\n"), a_to_4_byte(buffer) + 4); #endif ph = (struct mmc_performance_header *)buffer; perfp = (struct mmc_performance *)(((char *)ph) + sizeof (struct mmc_performance_header)); i = (a_to_4_byte(buffer) -4)/sizeof (struct mmc_performance); if (i > amt) i = amt; end = 0; speed = 0; for (; --i >= 0; perfp++) { ul = a_to_u_4_byte(perfp->end_lba); if (ul > end) { end = ul; ul = a_to_u_4_byte(perfp->end_perf); speed = ul; } } if (endp) *endp = end; if (writep) *writep = speed; perfp = (struct mmc_performance *)(((char *)ph) + sizeof (struct mmc_performance_header)); i = (a_to_4_byte(buffer) -4)/sizeof (struct mmc_performance); if (i > scmd->cdb.cmd_cdb[9]) i = scmd->cdb.cmd_cdb[9]; if (xdebug > 1) error(_("CurSpeed Writeperf: %d\n"), i); else if (xdebug < 0) error(_("Write Performance:\n")); if (xdebug != 0) for (; --i >= 0; perfp++) { ul = a_to_u_4_byte(perfp->start_lba); error(_("START LBA: %7lu\n"), ul); ul = a_to_u_4_byte(perfp->end_lba); error(_("End LBA: %7lu\n"), ul); ul = a_to_u_4_byte(perfp->start_perf); error(_("Start Perf: %7lu == %lux %s\n"), ul, ul/ssp, mname); ul = a_to_u_4_byte(perfp->end_perf); error(_("END Perf: %7lu == %lux %s\n"), ul, ul/ssp, mname); error("\n"); } #ifdef XDEBUG scg_prbytes(_("Performance data:"), (Uchar *)buffer, scmd->size - scg_getresid(scgp)); #endif } doread: if (readp) { fillbytes((caddr_t) buffer, sizeof (buffer), '\0'); scgp->silent++; if (scsi_get_performance(scgp, buffer, 8+16, 1, 0x00, 0x00) < 0) { scgp->silent--; return (-1); } scgp->silent--; ph = (struct mmc_performance_header *)buffer; amt = (a_to_4_byte(ph->p_datalen) -4)/sizeof (struct mmc_performance); if (amt < 1) amt = 1; if (amt > MAX_AMT) amt = MAX_AMT; if (scsi_get_performance(scgp, buffer, 8+16*amt, amt, 0x00, 0x00) < 0) return (-1); #ifdef XDEBUG error(_("Bytes: %d\n"), scmd->size - scg_getresid(scgp)); error(_("header: %ld\n"), a_to_4_byte(buffer) + 4); #endif ph = (struct mmc_performance_header *)buffer; perfp = (struct mmc_performance *)(((char *)ph) + sizeof (struct mmc_performance_header)); i = (a_to_4_byte(buffer) -4)/sizeof (struct mmc_performance); if (i > amt) i = amt; end = 0; speed = 0; for (; --i >= 0; perfp++) { ul = a_to_u_4_byte(perfp->end_lba); if (ul > end) { end = ul; ul = a_to_u_4_byte(perfp->end_perf); speed = ul; } } if (readp) *readp = speed; i = (a_to_4_byte(buffer) -4)/sizeof (struct mmc_performance); if (i > scmd->cdb.cmd_cdb[9]) i = scmd->cdb.cmd_cdb[9]; if (xdebug > 1) error(_("CurSpeed Readperf: %d\n"), i); else if (xdebug < 0) error(_("Read Performance:\n")); if (xdebug != 0) for (; --i >= 0; perfp++) { ul = a_to_u_4_byte(perfp->start_lba); error(_("START LBA: %7lu\n"), ul); ul = a_to_u_4_byte(perfp->end_lba); error(_("End LBA: %7lu\n"), ul); ul = a_to_u_4_byte(perfp->start_perf); error(_("Start Perf: %7lu == %lux %s\n"), ul, ul/ssp, mname); ul = a_to_u_4_byte(perfp->end_perf); error(_("END Perf: %7lu == %lux %s\n"), ul, ul/ssp, mname); error("\n"); } #ifdef XDEBUG scg_prbytes(_("Performance data:"), (Uchar *)buffer, scmd->size - scg_getresid(scgp)); #endif } return (0); } LOCAL int scsi_set_streaming(scgp, readp, writep, endp) SCSI *scgp; Ulong *readp; Ulong *writep; Ulong *endp; { register struct scg_cmd *scmd = scgp->scmd; struct mmc_streaming str; struct mmc_streaming *sp = &str; fillbytes((caddr_t) scmd, sizeof (*scmd), '\0'); scmd->addr = (caddr_t) sp; scmd->size = sizeof (*sp); scmd->flags = SCG_DISRE_ENA; scmd->cdb_len = SC_G5_CDBLEN; scmd->sense_len = CCS_SENSE_LEN; scmd->cdb.g5_cdb.cmd = 0xB6; scmd->cdb.g5_cdb.lun = scg_lun(scgp); i_to_2_byte(&scmd->cdb.cmd_cdb[9], sizeof (*sp)); /* Sz not G5 alike */ scgp->cmdname = "set streaming"; fillbytes(sp, sizeof (*sp), '\0'); if (endp) i_to_4_byte(sp->end_lba, *endp); else i_to_4_byte(sp->end_lba, 0x7FFFFFFF); if (readp) i_to_4_byte(sp->read_size, *readp); else i_to_4_byte(sp->read_size, 0x7FFFFFFF); if (writep) i_to_4_byte(sp->write_size, *writep); else i_to_4_byte(sp->write_size, 0x7FFFFFFF); i_to_4_byte(sp->read_time, 1000); i_to_4_byte(sp->write_time, 1000); #ifdef DEBUG scg_prbytes(_("Streaming data:"), (Uchar *)sp, sizeof (*sp)); #endif return (scg_cmd(scgp)); } /* * set speed using the streaming descriptors */ EXPORT int speed_select_mdvd(scgp, readspeed, writespeed) SCSI *scgp; int readspeed; int writespeed; { Ulong end_lba = 0x7FFFFFFF; Ulong wspeed = writespeed; if (scsi_get_perf_maxspeed(scgp, (Ulong *)NULL, (Ulong *)NULL, &end_lba) < 0) return (-1); if (scsi_set_streaming(scgp, (Ulong *)NULL, &wspeed, &end_lba) < 0) return (-1); return (0); } LOCAL char * fname(code) Uint code; { UInt16_t i; for (i = 0; i < sizeof (fl) / sizeof (fl[0]); i++) { if (code == fl[i].code) return (fl[i].name); } return ("Unknown"); } LOCAL char * pname(code) Uint code; { UInt16_t i; for (i = 0; i < sizeof (pl) / sizeof (pl[0]); i++) { if (code == pl[i].code) return (pl[i].name); } return ("Unknown"); } LOCAL BOOL fname_known(code) Uint code; { UInt16_t i; for (i = 0; i < sizeof (fl) / sizeof (fl[0]); i++) { if (code == fl[i].code) return (TRUE); } return (FALSE); } LOCAL BOOL pname_known(code) Uint code; { UInt16_t i; for (i = 0; i < sizeof (pl) / sizeof (pl[0]); i++) { if (code == pl[i].code) return (TRUE); } return (FALSE); } EXPORT int print_features(scgp) SCSI *scgp; { Uchar fbuf[32 * 1024]; Uchar *p; Uchar *pend; int amt; int flen; int feature; int i; flen = get_conflen(scgp, 0, 0); if (flen < 0) return (-1); if (sizeof (fbuf) < flen) flen = sizeof (fbuf); fillbytes(fbuf, sizeof (fbuf), '\0'); scgp->silent++; i = get_configuration(scgp, (char *)fbuf, sizeof (fbuf), 0, 0); scgp->silent--; if (i < 0) return (-1); amt = sizeof (fbuf) - scg_getresid(scgp); p = fbuf; pend = &p[sizeof (fbuf) - scg_getresid(scgp)]; flen = a_to_u_4_byte(p); if ((flen+4) < amt) amt = flen+4; pend = &p[amt]; if (xdebug > 1) scg_prbytes(_("Features: "), fbuf, amt); feature = a_to_u_2_byte(&p[6]); if (xdebug > 0) printf(_("feature len: %d current profile 0x%04X len %lld\n"), flen, feature, (Llong)(pend - p)); p = fbuf + 8; /* Skip feature header */ while (p < pend) { int col; col = 0; feature = a_to_u_2_byte(p); if (xdebug > 0) col += printf(_("Feature: 0x%04X "), feature); else col += printf(_("Feature: ")); if (fname_known(feature)) col += printf("'%s' ", fname(feature)); else col += printf("0x%04X ", feature); col += printf("%s %s", p[2] & 1 ? _("(current)"):"", p[2] & 2 ? _("(persistent)"):""); if (feature == 0x108) col += printf(_(" Serial: '%.*s'"), p[3], &p[4]); if (xdebug > 1 && p[3]) { if (col < 50) printf("%*s", 50-col, ""); scg_fprbytes(stdout, _(" Data: "), &p[4], p[3]); } else { printf("\n"); } p += p[3]; p += 4; } return (0); } LOCAL char *fdt[] = { "Reserved (0)", "Unformated or Blank Media", "Formatted Media", "No Media Present or Unknown Capacity" }; EXPORT void print_format_capacities(scgp) SCSI *scgp; { Uchar b[1024]; int i; Uchar *p; fillbytes(b, sizeof (b), '\0'); scgp->silent++; i = read_format_capacities(scgp, (char *)b, sizeof (b)); scgp->silent--; if (i < 0) return; i = b[3] + 4; fillbytes(b, sizeof (b), '\0'); if (read_format_capacities(scgp, (char *)b, i) < 0) return; if (xdebug > 0) { i = b[3] + 4; scg_prbytes(_("Format cap: "), b, i); } i = b[3]; if (i > 0) { int cnt; UInt32_t n1; UInt32_t n2; printf(_("\n Capacity Blklen/Sparesz. Format-type Type\n")); for (p = &b[4]; i > 0; i -= 8, p += 8) { cnt = 0; n1 = a_to_u_4_byte(p); n2 = a_to_u_3_byte(&p[5]); printf("%12lu %16lu 0x%2.2X %s\n", (Ulong)n1, (Ulong)n2, (p[4] >> 2) & 0x3F, fdt[p[4] & 0x03]); } } } EXPORT int get_format_capacities(scgp, bp, cnt) SCSI *scgp; caddr_t bp; int cnt; { int len = sizeof (struct scsi_format_cap_header); struct scsi_format_cap_header *hp; fillbytes(bp, cnt, '\0'); if (cnt < len) return (-1); scgp->silent++; if (read_format_capacities(scgp, bp, len) < 0) { scgp->silent--; return (-1); } scgp->silent--; if (scg_getresid(scgp) > 0) return (-1); hp = (struct scsi_format_cap_header *)bp; len = hp->len; len += sizeof (struct scsi_format_cap_header); while (len > cnt) len -= sizeof (struct scsi_format_cap_desc); if (read_format_capacities(scgp, bp, len) < 0) return (-1); len -= scg_getresid(scgp); return (len); } EXPORT int read_format_capacities(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->cdb.g1_cdb.cmd = 0x23; scmd->cdb.g1_cdb.lun = scg_lun(scgp); g1_cdblen(&scmd->cdb.g1_cdb, cnt); scgp->cmdname = "read_format_capacities"; return (scg_cmd(scgp)); } EXPORT void przone(rp) struct rzone_info *rp; { int rsize = a_to_2_byte(rp->data_len)+2; if (rsize < 12) return; printf(_("rzone size: %d\n"), rsize); printf(_("rzone number: %d\n"), rp->rzone_num_msb * 256 + rp->rzone_num_lsb); printf(_("border number: %d\n"), rp->border_num_msb * 256 + rp->border_num_lsb); printf(_("ljrs: %d\n"), rp->ljrs); printf(_("track mode: %d copy: %d\n"), rp->trackmode, rp->copy); printf(_("damage: %d\n"), rp->damage); printf(_("reserved track: %d blank: %d incremental: %d fp: %d\n"), rp->rt, rp->blank, rp->incremental, rp->fp); printf(_("data mode: %d\n"), rp->datamode); printf(_("lra valid: %d\n"), rp->lra_v); printf(_("nwa valid: %d\n"), rp->nwa_v); printf(_("rzone start: %ld\n"), a_to_4_byte(rp->rzone_start)); printf(_("next wr addr: %ld\n"), a_to_4_byte(rp->next_recordable_addr)); printf(_("free blocks: %ld\n"), a_to_4_byte(rp->free_blocks)); printf(_("blocking factor: %ld\n"), a_to_4_byte(rp->block_factor)); printf(_("rzone size: %ld\n"), a_to_4_byte(rp->rzone_size)); printf(_("last recorded addr: %ld\n"), a_to_4_byte(rp->last_recorded_addr)); if (rsize < 40) return; printf(_("read compat lba: %ld\n"), a_to_4_byte(rp->read_compat_lba)); if (rsize < 44) return; printf(_("next layerjmp addr: %ld\n"), a_to_4_byte(rp->next_layer_jump)); if (rsize < 48) return; printf(_("last layerjmp addr: %ld\n"), a_to_4_byte(rp->last_layer_jump)); } EXPORT int get_diskinfo(scgp, dip, cnt) SCSI *scgp; struct disk_info *dip; int cnt; { int len; int ret; fillbytes((caddr_t)dip, 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_disk_info(scgp, (caddr_t)dip, 4) < 0) return (-1); len = a_to_u_2_byte(dip->data_len); len += 2; if (len > cnt) len = cnt; ret = read_disk_info(scgp, (caddr_t)dip, len); #ifdef DEBUG if (lverbose > 1) scg_prbytes(_("Disk info:"), (Uchar *)dip, len-scg_getresid(scgp)); #endif return (ret); } #define IS(what, flag) printf(_("Disk Is %s%s\n"), flag?"":_("not "), what); LOCAL char res[] = "reserved"; EXPORT char * get_ses_type(ses_type) int ses_type; { static char ret[16]; switch (ses_type) { case SES_DA_ROM: return ("CD-DA or CD-ROM"); case SES_CDI: return ("CDI"); case SES_XA: return ("CD-ROM XA"); case SES_UNDEF: return ("undefined"); default: js_snprintf(ret, sizeof (ret), "%s: 0x%2.2X", res, ses_type); return (ret); } } EXPORT void print_diskinfo(dip, is_cd) struct disk_info *dip; BOOL is_cd; { static char *dt_name[] = { "standard", "track resources", "POW resources", res, res, res, res, res }; static char *ds_name[] = { "empty", "incomplete/appendable", "complete", "illegal" }; static char *ss_name[] = { "empty", "incomplete/appendable", "illegal", "complete", }; static char *fd_name[] = { "none", "incomplete", "in progress", "completed", }; IS(_("erasable"), dip->erasable); printf(_("data type: %s\n"), dt_name[dip->dtype]); printf(_("disk status: %s\n"), ds_name[dip->disk_status]); printf(_("session status: %s\n"), ss_name[dip->sess_status]); printf(_("BG format status: %s\n"), fd_name[dip->bg_format_stat]); printf(_("first track: %d\n"), dip->first_track); printf(_("number of sessions: %d\n"), dip->numsess + dip->numsess_msb * 256); printf(_("first track in last sess: %d\n"), dip->first_track_ls + dip->first_track_ls_msb * 256); printf(_("last track in last sess: %d\n"), dip->last_track_ls + dip->last_track_ls_msb * 256); IS(_("unrestricted"), dip->uru); printf(_("Disk type: ")); if (is_cd) { printf("%s", get_ses_type(dip->disk_type)); } else { printf(_("DVD, HD-DVD or BD")); } printf("\n"); if (dip->did_v) printf(_("Disk id: 0x%lX\n"), a_to_u_4_byte(dip->disk_id)); if (is_cd) { printf(_("last start of lead in: %ld\n"), msf_to_lba(dip->last_lead_in[1], dip->last_lead_in[2], dip->last_lead_in[3], FALSE)); printf(_("last start of lead out: %ld\n"), msf_to_lba(dip->last_lead_out[1], dip->last_lead_out[2], dip->last_lead_out[3], TRUE)); } if (dip->dbc_v) printf(_("Disk bar code: 0x%lX%lX\n"), a_to_u_4_byte(dip->disk_barcode), a_to_u_4_byte(&dip->disk_barcode[4])); if (dip->dac_v) printf(_("Disk appl. code: %d\n"), dip->disk_appl_code); if (dip->num_opc_entries > 0) { printf(_("OPC table:\n")); } } EXPORT int prdiskstatus(scgp, dp, is_cd) SCSI *scgp; cdr_t *dp; BOOL is_cd; { struct disk_info di; struct rzone_info rz; int sessions; int track; int tracks; int t; int s; long raddr; long lastaddr = -1; long lastsess = -1; long leadout = -1; long lo_sess = 0; long nwa = -1; long rsize = -1; long border_size = -1; int profile; profile = get_curprofile(scgp); if (profile > 0) { int mt = get_mediatype(scgp); printf(_("Mounted media class: %s\n"), get_mclassname(mt)); if (pname_known(profile)) { printf(_("Mounted media type: %s\n"), pname(profile)); } } get_diskinfo(scgp, &di, sizeof (di)); print_diskinfo(&di, is_cd); sessions = di.numsess + di.numsess_msb * 256; tracks = di.last_track_ls + di.last_track_ls_msb * 256; printf(_("\nTrack Sess Type Start Addr End Addr Size\n")); printf(_("==============================================\n")); fillbytes((caddr_t)&rz, sizeof (rz), '\0'); for (t = di.first_track; t <= tracks; t++) { fillbytes((caddr_t)&rz, sizeof (rz), '\0'); get_trackinfo(scgp, (caddr_t)&rz, TI_TYPE_TRACK, t, sizeof (rz)); if (lverbose > 1) przone(&rz); track = rz.rzone_num_lsb + rz.rzone_num_msb * 256; s = rz.border_num_lsb + rz.border_num_msb * 256; raddr = a_to_4_byte(rz.rzone_start); if (rsize >= 0) border_size = raddr - (lastaddr+rsize); if (!rz.blank && s > lastsess) { /* First track in last sess ? */ lastaddr = raddr; lastsess = s; } nwa = a_to_4_byte(rz.next_recordable_addr); rsize = a_to_4_byte(rz.rzone_size); if (!rz.blank) { leadout = raddr + rsize; lo_sess = s; } printf("%5d %5d %-6s %-10ld %-10ld %ld", track, s, rz.blank ? _("Blank") : rz.trackmode & 4 ? _("Data") : _("Audio"), raddr, raddr + rsize -1, rsize); if (lverbose > 0) printf(" %10ld", border_size); printf("\n"); } printf("\n"); if (lastaddr >= 0) printf(_("Last session start address: %ld\n"), lastaddr); if (leadout >= 0) printf(_("Last session leadout start address: %ld\n"), leadout); if (rz.nwa_v) { printf(_("Next writable address: %ld\n"), nwa); printf(_("Remaining writable size: %ld\n"), rsize); } return (0); } EXPORT int sessstatus(scgp, is_cd, offp, nwap) SCSI *scgp; BOOL is_cd; long *offp; long *nwap; { struct disk_info di; struct rzone_info rz; int sessions; int track; int tracks; int t; int s; long raddr; long lastaddr = -1; long lastsess = -1; long leadout = -1; long lo_sess = 0; long nwa = -1; long rsize = -1; long border_size = -1; if (get_diskinfo(scgp, &di, sizeof (di)) < 0) return (-1); sessions = di.numsess + di.numsess_msb * 256; tracks = di.last_track_ls + di.last_track_ls_msb * 256; fillbytes((caddr_t)&rz, sizeof (rz), '\0'); for (t = di.first_track; t <= tracks; t++) { fillbytes((caddr_t)&rz, sizeof (rz), '\0'); if (get_trackinfo(scgp, (caddr_t)&rz, TI_TYPE_TRACK, t, sizeof (rz)) < 0) return (-1); track = rz.rzone_num_lsb + rz.rzone_num_msb * 256; s = rz.border_num_lsb + rz.border_num_msb * 256; raddr = a_to_4_byte(rz.rzone_start); if (rsize >= 0) border_size = raddr - (lastaddr+rsize); if (!rz.blank && s > lastsess) { /* First track in last sess ? */ lastaddr = raddr; lastsess = s; } nwa = a_to_4_byte(rz.next_recordable_addr); rsize = a_to_4_byte(rz.rzone_size); if (!rz.blank) { leadout = raddr + rsize; lo_sess = s; } } if (lastaddr >= 0 && offp != NULL) *offp = lastaddr; if (rz.nwa_v && nwap != NULL) *nwap = nwa; return (0); } EXPORT void print_performance_mmc(scgp) SCSI *scgp; { Ulong reads; Ulong writes; Ulong ends = 0x7FFFFFFF; int oxdebug = xdebug; /* * Do not try to fail with old drives... */ if (get_curprofile(scgp) < 0) return; if (xdebug == 0) xdebug = -1; printf(_("\nCurrent performance according to MMC get performance:\n")); scsi_get_perf_curspeed(scgp, &reads, &writes, &ends); printf(_("\nMaximum performance according to MMC get performance:\n")); scsi_get_perf_maxspeed(scgp, &reads, &writes, &ends); xdebug = oxdebug; }