cdrtools/cdrecord/scsi_mmc.c

1593 lines
35 KiB
C
Raw Normal View History

2025-06-15 04:19:58 +08:00
/* @(#)scsi_mmc.c 1.51 10/12/19 Copyright 2002-2010 J. Schilling */
#include <schily/mconfig.h>
#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 <schily/mconfig.h>
#include <schily/stdio.h>
#include <schily/standard.h>
#include <schily/stdlib.h>
#include <schily/unistd.h>
#include <schily/fcntl.h>
#include <schily/errno.h>
#include <schily/string.h>
#include <schily/time.h>
#include <schily/utypes.h>
#include <schily/btorder.h>
#include <schily/intcvt.h>
#include <schily/schily.h>
#include <schily/nlsdefs.h>
#include <scg/scgcmd.h>
#include <scg/scsidefs.h>
#include <scg/scsireg.h>
#include <scg/scsitransp.h>
#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;
}