1249 lines
30 KiB
C
1249 lines
30 KiB
C
/* @(#)scsi-bsd.c 1.53 15/10/08 Copyright 1997-2015 J. Schilling */
|
|
#ifndef lint
|
|
static char __sccsid[] =
|
|
"@(#)scsi-bsd.c 1.53 15/10/08 Copyright 1997-2015 J. Schilling";
|
|
#endif
|
|
/*
|
|
* Interface for the NetBSD/FreeBSD/OpenBSD generic SCSI implementation.
|
|
*
|
|
* This is a hack, that tries to emulate the functionality
|
|
* of the scg driver.
|
|
* The SCSI tranport of the generic *BSD implementation is very
|
|
* similar to the SCSI command transport of the
|
|
* 6 years older scg driver.
|
|
*
|
|
* Warning: you may change this source, but if you do that
|
|
* you need to change the _scg_version and _scg_auth* string below.
|
|
* You may not return "schily" for an SCG_AUTHOR request anymore.
|
|
* Choose your name instead of "schily" and make clear that the version
|
|
* string is related to a modified source.
|
|
*
|
|
* Copyright (c) 1997-2015 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.
|
|
* A copy of the CDDL is also available via the Internet at
|
|
* http://www.opensource.org/licenses/cddl1.txt
|
|
*
|
|
* The following exceptions apply:
|
|
* CDDL §3.6 needs to be replaced by: "You may create a Larger Work by
|
|
* combining Covered Software with other code if all other code is governed by
|
|
* the terms of a license that is OSI approved (see www.opensource.org) and
|
|
* you may distribute the Larger Work as a single product. In such a case,
|
|
* You must make sure the requirements of this License are fulfilled for
|
|
* the Covered Software."
|
|
*
|
|
* When distributing Covered Code, include this CDDL HEADER in each
|
|
* file and include the License file CDDL.Schily.txt from this distribution.
|
|
*/
|
|
|
|
#ifndef HAVE_CAMLIB_H
|
|
|
|
#undef sense
|
|
#include <sys/scsiio.h>
|
|
#if defined(__NetBSD__)
|
|
#ifdef USE_GETRAWPARTITION
|
|
#include <util.h>
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
* Warning: you may change this source, but if you do that
|
|
* you need to change the _scg_version and _scg_auth* string below.
|
|
* You may not return "schily" for an SCG_AUTHOR request anymore.
|
|
* Choose your name instead of "schily" and make clear that the version
|
|
* string is related to a modified source.
|
|
*/
|
|
LOCAL char _scg_trans_version[] = "scsi-bsd.c-1.53"; /* The version for this transport*/
|
|
|
|
#define MAX_SCG 32 /* Max # of SCSI controllers */
|
|
#define MAX_TGT 16
|
|
#define MAX_LUN 8
|
|
|
|
struct scg_local {
|
|
short scgfiles[MAX_SCG][MAX_TGT][MAX_LUN];
|
|
};
|
|
#define scglocal(p) ((struct scg_local *)((p)->local))
|
|
|
|
/*#define MAX_DMA_BSD (32*1024)*/
|
|
#define MAX_DMA_BSD (60*1024) /* More seems to make problems */
|
|
|
|
#if defined(__NetBSD__) && defined(TYPE_ATAPI)
|
|
/*
|
|
* NetBSD 1.3 has a merged SCSI/ATAPI system, so this structure
|
|
* is slightly different.
|
|
*/
|
|
#define MAYBE_ATAPI
|
|
#define SADDR_ISSCSI(a) ((a).type == TYPE_SCSI)
|
|
|
|
#define SADDR_BUS(a) (SADDR_ISSCSI(a)?(a).addr.scsi.scbus:(MAX_SCG-1))
|
|
#define SADDR_TARGET(a) (SADDR_ISSCSI(a)?(a).addr.scsi.target:(a).addr.atapi.atbus*2+(a).addr.atapi.drive)
|
|
#define SADDR_LUN(a) (SADDR_ISSCSI(a)?(a).addr.scsi.lun:0)
|
|
#else
|
|
|
|
#if defined(__OpenBSD__) && defined(TYPE_ATAPI)
|
|
#define SADDR_ISSCSI(a) ((a).type == TYPE_SCSI)
|
|
#else
|
|
#define SADDR_ISSCSI(a) (1)
|
|
#endif
|
|
|
|
#define SADDR_BUS(a) (a).scbus
|
|
#define SADDR_TARGET(a) (a).target
|
|
#define SADDR_LUN(a) (a).lun
|
|
#endif /* __NetBSD__ && TYPE_ATAPI */
|
|
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
LOCAL int getslice __PR((int n));
|
|
#endif
|
|
LOCAL BOOL scg_setup __PR((SCSI *scgp, int f, int busno, int tgt, int tlun));
|
|
|
|
/*
|
|
* Return version information for the low level SCSI transport code.
|
|
* This has been introduced to make it easier to trace down problems
|
|
* in applications.
|
|
*/
|
|
LOCAL char *
|
|
scgo_version(scgp, what)
|
|
SCSI *scgp;
|
|
int what;
|
|
{
|
|
if (scgp != (SCSI *)0) {
|
|
switch (what) {
|
|
|
|
case SCG_VERSION:
|
|
return (_scg_trans_version);
|
|
/*
|
|
* If you changed this source, you are not allowed to
|
|
* return "schily" for the SCG_AUTHOR request.
|
|
*/
|
|
case SCG_AUTHOR:
|
|
return (_scg_auth_schily);
|
|
case SCG_SCCS_ID:
|
|
return (__sccsid);
|
|
}
|
|
}
|
|
return ((char *)0);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_help(scgp, f)
|
|
SCSI *scgp;
|
|
FILE *f;
|
|
{
|
|
__scg_help(f, "SCIOCCOMMAND", "SCSI for devices known by *BSD",
|
|
#if defined(__OpenBSD__)
|
|
"", "device or bus,target,lun", "/dev/rcd0c:@ or 1,2,0", FALSE, TRUE);
|
|
#else
|
|
"", "device or bus,target,lun", "/dev/rcd0a:@ or 1,2,0", FALSE, TRUE);
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_open(scgp, device)
|
|
SCSI *scgp;
|
|
char *device;
|
|
{
|
|
int busno = scg_scsibus(scgp);
|
|
int tgt = scg_target(scgp);
|
|
int tlun = scg_lun(scgp);
|
|
register int f;
|
|
register int b;
|
|
register int t;
|
|
register int l;
|
|
register int nopen = 0;
|
|
char dev_name[64];
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
#ifdef USE_GETRAWPARTITION
|
|
int myslicename = getrawpartition();
|
|
#else
|
|
int myslicename = 0;
|
|
#endif
|
|
#endif
|
|
|
|
if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
|
|
errno = EINVAL;
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Illegal value for busno, target or lun '%d,%d,%d'",
|
|
busno, tgt, tlun);
|
|
return (-1);
|
|
}
|
|
|
|
if (scgp->local == NULL) {
|
|
scgp->local = malloc(sizeof (struct scg_local));
|
|
if (scgp->local == NULL)
|
|
return (0);
|
|
|
|
for (b = 0; b < MAX_SCG; b++) {
|
|
for (t = 0; t < MAX_TGT; t++) {
|
|
for (l = 0; l < MAX_LUN; l++)
|
|
scglocal(scgp)->scgfiles[b][t][l] = (short)-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
|
|
goto openbydev;
|
|
|
|
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
/*
|
|
* I know of no method in NetBSD/OpenBSD to probe the scsibus and get
|
|
* the mapping busnumber,target,lun --> devicename.
|
|
*
|
|
* For now, implement a true brute force hack to find cdroms to allow
|
|
* cdrecord to work, but libscg is a generic SCSI transport lib.
|
|
*
|
|
* Note that this method only finds cd0-cd9. For a more correct
|
|
* implementation we would at least need to also scan the disk and tape
|
|
* devices.
|
|
*
|
|
* This still does not include scanners and other SCSI devices.
|
|
* Help is needed!
|
|
*/
|
|
if (busno >= 0 && tgt >= 0 && tlun >= 0) {
|
|
struct scsi_addr mysaddr;
|
|
|
|
for (l = 0; l < 10; l++) {
|
|
nextslice1:
|
|
sprintf(dev_name, "/dev/rcd%d%c", l, 'a' + myslicename);
|
|
f = open(dev_name, O_RDWR);
|
|
if (f >= 0) {
|
|
if (ioctl(f, SCIOCIDENTIFY, &mysaddr) < 0) {
|
|
#ifndef USE_GETRAWPARTITION
|
|
if (errno == ENOTTY && myslicename < 16) {
|
|
close(f);
|
|
myslicename = getslice(l);
|
|
goto nextslice1;
|
|
}
|
|
#endif
|
|
close(f);
|
|
errno = EINVAL;
|
|
return (0);
|
|
}
|
|
if (busno == SADDR_BUS(mysaddr) &&
|
|
tgt == SADDR_TARGET(mysaddr) &&
|
|
tlun == SADDR_LUN(mysaddr)) {
|
|
scglocal(scgp)->scgfiles[busno][tgt][tlun] = f;
|
|
return (1);
|
|
}
|
|
} else {
|
|
#if defined(__OpenBSD__) && defined(ENOMEDIUM) && defined(ENXIO)
|
|
if ((errno == ENOMEDIUM || errno == ENXIO) && myslicename < 16) {
|
|
myslicename = getslice(l);
|
|
goto nextslice1;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
} else for (l = 0; l < 10; l++) {
|
|
struct scsi_addr mysaddr;
|
|
|
|
nextslice2:
|
|
sprintf(dev_name, "/dev/rcd%d%c", l, 'a' + myslicename);
|
|
f = open(dev_name, O_RDWR);
|
|
if (f >= 0) {
|
|
if (ioctl(f, SCIOCIDENTIFY, &mysaddr) < 0) {
|
|
#ifndef USE_GETRAWPARTITION
|
|
if (errno == ENOTTY && myslicename < 16) {
|
|
close(f);
|
|
myslicename = getslice(l);
|
|
goto nextslice2;
|
|
}
|
|
#endif
|
|
close(f);
|
|
errno = EINVAL;
|
|
return (0);
|
|
}
|
|
if (scg_setup(scgp, f, busno, tgt, tlun))
|
|
nopen++;
|
|
} else {
|
|
#if defined(__OpenBSD__) && defined(ENOMEDIUM) && defined(ENXIO)
|
|
if ((errno == ENOMEDIUM || errno == ENXIO) && myslicename < 16) {
|
|
myslicename = getslice(l);
|
|
goto nextslice2;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#else /* ! __NetBSD__ || __OpenBSD__ */
|
|
if (busno >= 0 && tgt >= 0 && tlun >= 0) {
|
|
|
|
js_snprintf(dev_name, sizeof (dev_name),
|
|
"/dev/su%d-%d-%d", busno, tgt, tlun);
|
|
f = open(dev_name, O_RDWR);
|
|
if (f < 0) {
|
|
goto openbydev;
|
|
}
|
|
scglocal(scgp)->scgfiles[busno][tgt][tlun] = f;
|
|
return (1);
|
|
|
|
} else for (b = 0; b < MAX_SCG; b++) {
|
|
for (t = 0; t < MAX_TGT; t++) {
|
|
for (l = 0; l < MAX_LUN; l++) {
|
|
js_snprintf(dev_name, sizeof (dev_name),
|
|
"/dev/su%d-%d-%d", b, t, l);
|
|
f = open(dev_name, O_RDWR);
|
|
/* error("open (%s) = %d\n", dev_name, f);*/
|
|
|
|
if (f < 0) {
|
|
if (errno != ENOENT &&
|
|
errno != ENXIO &&
|
|
errno != ENODEV) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Cannot open '%s'",
|
|
dev_name);
|
|
return (0);
|
|
}
|
|
} else {
|
|
if (scg_setup(scgp, f, b, t, l))
|
|
nopen++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif /* ! __NetBSD__ || __OpebBSD__ */
|
|
/*
|
|
* Could not open /dev/su-* or got dev=devname:b,l,l / dev=devname:@,l
|
|
* We do the apropriate tests and try our best.
|
|
*/
|
|
openbydev:
|
|
if (nopen == 0) {
|
|
struct scsi_addr saddr;
|
|
|
|
if (device == NULL || device[0] == '\0')
|
|
return (0);
|
|
f = open(device, O_RDWR);
|
|
if (f < 0) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Cannot open '%s'",
|
|
device);
|
|
return (0);
|
|
}
|
|
if (tgt == -2) {
|
|
if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) {
|
|
close(f);
|
|
errno = EINVAL;
|
|
return (0);
|
|
}
|
|
busno = SADDR_BUS(saddr);
|
|
tgt = SADDR_TARGET(saddr);
|
|
if ((tlun >= 0) && (tlun != SADDR_LUN(saddr))) {
|
|
close(f);
|
|
errno = EINVAL;
|
|
return (0);
|
|
}
|
|
tlun = SADDR_LUN(saddr);
|
|
scg_settarget(scgp, busno, tgt, tlun);
|
|
}
|
|
if (scg_setup(scgp, f, busno, tgt, tlun))
|
|
nopen++;
|
|
}
|
|
return (nopen);
|
|
}
|
|
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
/*
|
|
* Avoid to call getrawpartition() because this would force us to
|
|
* link against libutil.
|
|
*/
|
|
LOCAL int
|
|
getslice(n)
|
|
int n;
|
|
{
|
|
struct scsi_addr saddr;
|
|
char dev_name[64];
|
|
int slice;
|
|
int f;
|
|
|
|
for (slice = 0; slice < 16; slice++) {
|
|
sprintf(dev_name, "/dev/rcd%d%c", n, 'a' + slice);
|
|
f = open(dev_name, O_RDWR);
|
|
if (f >= 0) {
|
|
if (ioctl(f, SCIOCIDENTIFY, &saddr) >= 0) {
|
|
close(f);
|
|
break;
|
|
}
|
|
close(f);
|
|
}
|
|
}
|
|
return (slice);
|
|
}
|
|
#endif
|
|
|
|
LOCAL int
|
|
scgo_close(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
register int f;
|
|
register int b;
|
|
register int t;
|
|
register int l;
|
|
|
|
if (scgp->local == NULL)
|
|
return (-1);
|
|
|
|
for (b = 0; b < MAX_SCG; b++) {
|
|
for (t = 0; t < MAX_TGT; t++) {
|
|
for (l = 0; l < MAX_LUN; l++) {
|
|
f = scglocal(scgp)->scgfiles[b][t][l];
|
|
if (f >= 0)
|
|
close(f);
|
|
scglocal(scgp)->scgfiles[b][t][l] = (short)-1;
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
LOCAL BOOL
|
|
scg_setup(scgp, f, busno, tgt, tlun)
|
|
SCSI *scgp;
|
|
int f;
|
|
int busno;
|
|
int tgt;
|
|
int tlun;
|
|
{
|
|
struct scsi_addr saddr;
|
|
int Bus;
|
|
int Target;
|
|
int Lun;
|
|
BOOL onetarget = FALSE;
|
|
|
|
if (scg_scsibus(scgp) >= 0 && scg_target(scgp) >= 0 && scg_lun(scgp) >= 0)
|
|
onetarget = TRUE;
|
|
|
|
if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) {
|
|
errmsg("Cannot get SCSI addr.\n");
|
|
close(f);
|
|
return (FALSE);
|
|
}
|
|
Bus = SADDR_BUS(saddr);
|
|
Target = SADDR_TARGET(saddr);
|
|
Lun = SADDR_LUN(saddr);
|
|
|
|
if (scgp->debug > 0) {
|
|
js_fprintf((FILE *)scgp->errfile,
|
|
"Bus: %d Target: %d Lun: %d\n", Bus, Target, Lun);
|
|
}
|
|
|
|
if (Bus >= MAX_SCG || Target >= MAX_TGT || Lun >= MAX_LUN) {
|
|
close(f);
|
|
return (FALSE);
|
|
}
|
|
|
|
if (scglocal(scgp)->scgfiles[Bus][Target][Lun] == (short)-1) {
|
|
scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)f;
|
|
return (TRUE);
|
|
}
|
|
|
|
if (onetarget) {
|
|
if (Bus == busno && Target == tgt && Lun == tlun) {
|
|
return (TRUE);
|
|
} else {
|
|
scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)-1;
|
|
close(f);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
LOCAL long
|
|
scgo_maxdma(scgp, amt)
|
|
SCSI *scgp;
|
|
long amt;
|
|
{
|
|
long maxdma = MAX_DMA_BSD;
|
|
|
|
return (maxdma);
|
|
}
|
|
|
|
LOCAL void *
|
|
scgo_getbuf(scgp, amt)
|
|
SCSI *scgp;
|
|
long amt;
|
|
{
|
|
if (scgp->debug > 0) {
|
|
js_fprintf((FILE *)scgp->errfile,
|
|
"scgo_getbuf: %ld bytes\n", amt);
|
|
}
|
|
scgp->bufbase = valloc((size_t)(amt));
|
|
return (scgp->bufbase);
|
|
}
|
|
|
|
LOCAL void
|
|
scgo_freebuf(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
if (scgp->bufbase)
|
|
free(scgp->bufbase);
|
|
scgp->bufbase = NULL;
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_numbus(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
return (MAX_SCG);
|
|
}
|
|
|
|
LOCAL BOOL
|
|
scgo_havebus(scgp, busno)
|
|
SCSI *scgp;
|
|
int busno;
|
|
{
|
|
register int t;
|
|
register int l;
|
|
|
|
if (busno < 0 || busno >= MAX_SCG)
|
|
return (FALSE);
|
|
|
|
if (scgp->local == NULL)
|
|
return (FALSE);
|
|
|
|
for (t = 0; t < MAX_TGT; t++) {
|
|
for (l = 0; l < MAX_LUN; l++)
|
|
if (scglocal(scgp)->scgfiles[busno][t][l] >= 0)
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_fileno(scgp, busno, tgt, tlun)
|
|
SCSI *scgp;
|
|
int busno;
|
|
int tgt;
|
|
int tlun;
|
|
{
|
|
if (busno < 0 || busno >= MAX_SCG ||
|
|
tgt < 0 || tgt >= MAX_TGT ||
|
|
tlun < 0 || tlun >= MAX_LUN)
|
|
return (-1);
|
|
|
|
if (scgp->local == NULL)
|
|
return (-1);
|
|
|
|
return ((int)scglocal(scgp)->scgfiles[busno][tgt][tlun]);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_initiator_id(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_isatapi(scgp)
|
|
SCSI *scgp;
|
|
|
|
{
|
|
#ifdef MAYBE_ATAPI
|
|
struct scsi_addr saddr;
|
|
|
|
if (ioctl(scgp->fd, SCIOCIDENTIFY, &saddr) < 0)
|
|
return (-1);
|
|
|
|
if (!SADDR_ISSCSI(saddr))
|
|
return (TRUE);
|
|
#endif
|
|
return (FALSE);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_reset(scgp, what)
|
|
SCSI *scgp;
|
|
int what;
|
|
{
|
|
if (what == SCG_RESET_NOP)
|
|
return (0);
|
|
if (what != SCG_RESET_BUS) {
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
/*
|
|
* XXX Does this reset TGT or BUS ???
|
|
*/
|
|
return (ioctl(scgp->fd, SCIOCRESET, 0));
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_send(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
struct scg_cmd *sp = scgp->scmd;
|
|
scsireq_t req;
|
|
register long *lp1;
|
|
register long *lp2;
|
|
int ret = 0;
|
|
|
|
/* js_fprintf((FILE *)scgp->errfile, "fd: %d\n", scgp->fd);*/
|
|
if (scgp->fd < 0) {
|
|
sp->error = SCG_FATAL;
|
|
return (0);
|
|
}
|
|
req.flags = SCCMD_ESCAPE; /* We set the SCSI cmd len */
|
|
if (sp->flags & SCG_RECV_DATA)
|
|
req.flags |= SCCMD_READ;
|
|
else if (sp->size > 0)
|
|
req.flags |= SCCMD_WRITE;
|
|
|
|
req.timeout = sp->timeout * 1000;
|
|
lp1 = (long *)sp->cdb.cmd_cdb;
|
|
lp2 = (long *)req.cmd;
|
|
*lp2++ = *lp1++;
|
|
*lp2++ = *lp1++;
|
|
*lp2++ = *lp1++;
|
|
*lp2++ = *lp1++;
|
|
req.cmdlen = sp->cdb_len;
|
|
req.databuf = sp->addr;
|
|
req.datalen = sp->size;
|
|
req.datalen_used = 0;
|
|
fillbytes(req.sense, sizeof (req.sense), '\0');
|
|
if (sp->sense_len > sizeof (req.sense))
|
|
req.senselen = sizeof (req.sense);
|
|
else if (sp->sense_len < 0)
|
|
req.senselen = 0;
|
|
else
|
|
req.senselen = sp->sense_len;
|
|
req.senselen_used = 0;
|
|
req.status = 0;
|
|
req.retsts = 0;
|
|
req.error = 0;
|
|
|
|
if (ioctl(scgp->fd, SCIOCCOMMAND, (void *)&req) < 0) {
|
|
ret = -1;
|
|
sp->ux_errno = geterrno();
|
|
if (sp->ux_errno != ENOTTY)
|
|
ret = 0;
|
|
} else {
|
|
sp->ux_errno = 0;
|
|
if (req.retsts != SCCMD_OK)
|
|
sp->ux_errno = EIO;
|
|
}
|
|
fillbytes(&sp->scb, sizeof (sp->scb), '\0');
|
|
fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0');
|
|
sp->resid = req.datalen - req.datalen_used;
|
|
sp->sense_count = req.senselen_used;
|
|
if (sp->sense_count > SCG_MAX_SENSE)
|
|
sp->sense_count = SCG_MAX_SENSE;
|
|
movebytes(req.sense, sp->u_sense.cmd_sense, sp->sense_count);
|
|
sp->u_scb.cmd_scb[0] = req.status;
|
|
|
|
switch (req.retsts) {
|
|
|
|
case SCCMD_OK:
|
|
#ifdef BSD_SCSI_SENSE_BUG
|
|
sp->u_scb.cmd_scb[0] = 0;
|
|
sp->ux_errno = 0;
|
|
#endif
|
|
sp->error = SCG_NO_ERROR; break;
|
|
case SCCMD_TIMEOUT: sp->error = SCG_TIMEOUT; break;
|
|
default:
|
|
case SCCMD_BUSY: sp->error = SCG_RETRYABLE; break;
|
|
case SCCMD_SENSE: sp->error = SCG_RETRYABLE; break;
|
|
case SCCMD_UNKNOWN: sp->error = SCG_FATAL; break;
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
#define sense u_sense.Sense
|
|
|
|
#else /* BSD_CAM */
|
|
/*
|
|
* Interface for the FreeBSD CAM passthrough device.
|
|
*
|
|
* Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
|
|
* Copyright (c) 1998 Kenneth D. Merry <ken@kdm.org>
|
|
* Copyright (c) 1998-2011 Joerg Schilling <joerg.schilling@fokus.fraunhofer.de>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
#undef sense
|
|
#define scsi_sense CAM_scsi_sense
|
|
#define scsi_inquiry CAM_scsi_inquiry
|
|
#include <sys/param.h>
|
|
#include <cam/cam.h>
|
|
#include <cam/cam_ccb.h>
|
|
#include <cam/scsi/scsi_message.h>
|
|
#include <cam/scsi/scsi_pass.h>
|
|
#include <camlib.h>
|
|
|
|
/*
|
|
* Warning: you may change this source, but if you do that
|
|
* you need to change the _scg_version and _scg_auth* string below.
|
|
* You may not return "schily" for an SCG_AUTHOR request anymore.
|
|
* Choose your name instead of "schily" and make clear that the version
|
|
* string is related to a modified source.
|
|
*/
|
|
LOCAL char _scg_trans_version[] = "scsi-bsd.c-1.53"; /* The version for this transport*/
|
|
|
|
#define CAM_MAXDEVS 128
|
|
struct scg_local {
|
|
struct cam_device *cam_devices[CAM_MAXDEVS + 1];
|
|
};
|
|
#define scglocal(p) ((struct scg_local *)((p)->local))
|
|
|
|
/*
|
|
* Return version information for the low level SCSI transport code.
|
|
* This has been introduced to make it easier to trace down problems
|
|
* in applications.
|
|
*/
|
|
LOCAL char *
|
|
scgo_version(scgp, what)
|
|
SCSI *scgp;
|
|
int what;
|
|
{
|
|
if (scgp != (SCSI *)0) {
|
|
switch (what) {
|
|
|
|
case SCG_VERSION:
|
|
return (_scg_trans_version);
|
|
/*
|
|
* If you changed this source, you are not allowed to
|
|
* return "schily" for the SCG_AUTHOR request.
|
|
*/
|
|
case SCG_AUTHOR:
|
|
return (_scg_auth_schily);
|
|
case SCG_SCCS_ID:
|
|
return (__sccsid);
|
|
}
|
|
}
|
|
return ((char *)0);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_help(scgp, f)
|
|
SCSI *scgp;
|
|
FILE *f;
|
|
{
|
|
__scg_help(f, "CAM", "Generic transport independent SCSI (Common Access Method)",
|
|
"", "bus,target,lun", "1,2,0", TRUE, FALSE);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Build a list of everything we can find.
|
|
*/
|
|
LOCAL int
|
|
scgo_open(scgp, device)
|
|
SCSI *scgp;
|
|
char *device;
|
|
{
|
|
int busno = scg_scsibus(scgp);
|
|
int tgt = scg_target(scgp);
|
|
int tlun = scg_lun(scgp);
|
|
char name[16];
|
|
int unit;
|
|
int nopen = 0;
|
|
union ccb ccb;
|
|
int bufsize;
|
|
struct periph_match_pattern *match_pat;
|
|
int fd;
|
|
|
|
seterrno(0);
|
|
if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) {
|
|
errno = EINVAL;
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Open by 'devname' not supported on this OS");
|
|
return (-1);
|
|
}
|
|
|
|
if (scgp->local == NULL) {
|
|
scgp->local = malloc(sizeof (struct scg_local));
|
|
if (scgp->local == NULL)
|
|
return (0);
|
|
|
|
for (unit = 0; unit <= CAM_MAXDEVS; unit++) {
|
|
scglocal(scgp)->cam_devices[unit] = (struct cam_device *)-1;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* If we're not scanning the bus, just open one device.
|
|
*/
|
|
if (busno >= 0 && tgt >= 0 && tlun >= 0) {
|
|
scglocal(scgp)->cam_devices[0] = cam_open_btl(busno, tgt, tlun, O_RDWR, NULL);
|
|
if (scglocal(scgp)->cam_devices[0] == NULL)
|
|
return (-1);
|
|
nopen++;
|
|
return (nopen);
|
|
}
|
|
|
|
/*
|
|
* First open the transport layer device. There's no point in the
|
|
* rest of this if we can't open it.
|
|
*/
|
|
|
|
if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Open of %s failed", XPT_DEVICE);
|
|
return (-1);
|
|
}
|
|
fillbytes(&ccb, sizeof (union ccb), '\0');
|
|
|
|
/*
|
|
* Get a list of up to CAM_MAXDEVS passthrough devices in the
|
|
* system.
|
|
*/
|
|
ccb.ccb_h.func_code = XPT_DEV_MATCH;
|
|
ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
|
|
ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
|
|
ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
|
|
|
|
/*
|
|
* Setup the result buffer.
|
|
*/
|
|
bufsize = sizeof (struct dev_match_result) * CAM_MAXDEVS;
|
|
ccb.cdm.match_buf_len = bufsize;
|
|
ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize);
|
|
if (ccb.cdm.matches == NULL) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Couldn't malloc match buffer");
|
|
close(fd);
|
|
return (-1);
|
|
}
|
|
ccb.cdm.num_matches = 0;
|
|
|
|
/*
|
|
* Setup the pattern buffer. We're matching against all
|
|
* peripherals named "pass".
|
|
*/
|
|
ccb.cdm.num_patterns = 1;
|
|
ccb.cdm.pattern_buf_len = sizeof (struct dev_match_pattern);
|
|
ccb.cdm.patterns = (struct dev_match_pattern *)malloc(
|
|
sizeof (struct dev_match_pattern));
|
|
if (ccb.cdm.patterns == NULL) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Couldn't malloc pattern buffer");
|
|
close(fd);
|
|
free(ccb.cdm.matches);
|
|
return (-1);
|
|
}
|
|
ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH;
|
|
match_pat = &ccb.cdm.patterns[0].pattern.periph_pattern;
|
|
js_snprintf(match_pat->periph_name, sizeof (match_pat->periph_name),
|
|
"pass");
|
|
match_pat->flags = PERIPH_MATCH_NAME;
|
|
|
|
if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) {
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"CAMIOCOMMAND ioctl failed");
|
|
close(fd);
|
|
free(ccb.cdm.matches);
|
|
free(ccb.cdm.patterns);
|
|
return (-1);
|
|
}
|
|
|
|
if ((ccb.ccb_h.status != CAM_REQ_CMP) ||
|
|
((ccb.cdm.status != CAM_DEV_MATCH_LAST) &&
|
|
(ccb.cdm.status != CAM_DEV_MATCH_MORE))) {
|
|
/* errmsgno(EX_BAD, "Got CAM error 0x%X, CDM error %d.\n",*/
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Got CAM error 0x%X, CDM error %d",
|
|
ccb.ccb_h.status, ccb.cdm.status);
|
|
close(fd);
|
|
free(ccb.cdm.matches);
|
|
free(ccb.cdm.patterns);
|
|
return (-1);
|
|
}
|
|
|
|
free(ccb.cdm.patterns);
|
|
close(fd);
|
|
|
|
for (unit = 0; unit < MIN(CAM_MAXDEVS, ccb.cdm.num_matches); unit++) {
|
|
struct periph_match_result *periph_result;
|
|
|
|
/*
|
|
* We shouldn't have anything other than peripheral
|
|
* matches in here. If we do, it means an error in the
|
|
* device matching code in the transport layer.
|
|
*/
|
|
if (ccb.cdm.matches[unit].type != DEV_MATCH_PERIPH) {
|
|
/* errmsgno(EX_BAD, "Kernel error! got periph match type %d!!\n",*/
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Kernel error! got periph match type %d!!",
|
|
ccb.cdm.matches[unit].type);
|
|
free(ccb.cdm.matches);
|
|
return (-1);
|
|
}
|
|
periph_result = &ccb.cdm.matches[unit].result.periph_result;
|
|
|
|
js_snprintf(name, sizeof (name),
|
|
"/dev/%s%d", periph_result->periph_name,
|
|
periph_result->unit_number);
|
|
|
|
/*
|
|
* cam_open_pass() avoids all lookup and translation from
|
|
* "regular device name" to passthrough unit number and
|
|
* just opens the device in question as a passthrough device.
|
|
*/
|
|
scglocal(scgp)->cam_devices[unit] = cam_open_pass(name, O_RDWR, NULL);
|
|
|
|
/*
|
|
* If we get back NULL from the open routine, it wasn't
|
|
* able to open the given passthrough device for one reason
|
|
* or another.
|
|
*/
|
|
if (scglocal(scgp)->cam_devices[unit] == NULL) {
|
|
#ifdef OLD
|
|
errmsgno(EX_BAD, "Error opening /dev/%s%d\n",
|
|
periph_result->periph_name,
|
|
periph_result->unit_number);
|
|
errmsgno(EX_BAD, "%s\n", cam_errbuf);
|
|
#endif
|
|
if (scgp->errstr)
|
|
js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE,
|
|
"Error opening /dev/%s%d Cam error '%s'",
|
|
periph_result->periph_name,
|
|
periph_result->unit_number,
|
|
cam_errbuf);
|
|
break;
|
|
}
|
|
nopen++;
|
|
}
|
|
|
|
free(ccb.cdm.matches);
|
|
return (nopen);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_close(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
register int i;
|
|
|
|
if (scgp->local == NULL)
|
|
return (-1);
|
|
|
|
for (i = 0; i <= CAM_MAXDEVS; i++) {
|
|
if (scglocal(scgp)->cam_devices[i] != (struct cam_device *)-1)
|
|
cam_close_device(scglocal(scgp)->cam_devices[i]);
|
|
scglocal(scgp)->cam_devices[i] = (struct cam_device *)-1;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
LOCAL long
|
|
scgo_maxdma(scgp, amt)
|
|
SCSI *scgp;
|
|
long amt;
|
|
{
|
|
#ifdef DFLTPHYS
|
|
return (DFLTPHYS);
|
|
#else
|
|
return (MAXPHYS);
|
|
#endif
|
|
}
|
|
|
|
LOCAL void *
|
|
scgo_getbuf(scgp, amt)
|
|
SCSI *scgp;
|
|
long amt;
|
|
{
|
|
if (scgp->debug > 0) {
|
|
js_fprintf((FILE *)scgp->errfile,
|
|
"scgo_getbuf: %ld bytes\n", amt);
|
|
}
|
|
scgp->bufbase = valloc((size_t)(amt));
|
|
return (scgp->bufbase);
|
|
}
|
|
|
|
LOCAL void
|
|
scgo_freebuf(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
if (scgp->bufbase)
|
|
free(scgp->bufbase);
|
|
scgp->bufbase = NULL;
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_numbus(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
unsigned int unit;
|
|
unsigned int maxbus = 0;
|
|
BOOL found_bus = FALSE;
|
|
|
|
if (scgp->local == NULL)
|
|
return (0);
|
|
|
|
/*
|
|
* There's a "cleaner" way to do this, using the matching code, but
|
|
* it would involve more code than this solution...
|
|
*/
|
|
for (unit = 0; scglocal(scgp)->cam_devices[unit] != (struct cam_device *)-1; unit++) {
|
|
if (scglocal(scgp)->cam_devices[unit] == NULL)
|
|
continue;
|
|
found_bus = TRUE;
|
|
/*
|
|
* path_id is unsigned, so maxbus cannot be initialized to -1.
|
|
*/
|
|
if (scglocal(scgp)->cam_devices[unit]->path_id > maxbus)
|
|
maxbus = scglocal(scgp)->cam_devices[unit]->path_id;
|
|
}
|
|
if (!found_bus)
|
|
return (0);
|
|
return (maxbus+1);
|
|
}
|
|
|
|
LOCAL BOOL
|
|
scgo_havebus(scgp, busno)
|
|
SCSI *scgp;
|
|
int busno;
|
|
{
|
|
int unit;
|
|
|
|
if (scgp->local == NULL)
|
|
return (FALSE);
|
|
|
|
/*
|
|
* There's a "cleaner" way to do this, using the matching code, but
|
|
* it would involve more code than this solution...
|
|
*/
|
|
for (unit = 0; scglocal(scgp)->cam_devices[unit] != (struct cam_device *)-1; unit++) {
|
|
if (scglocal(scgp)->cam_devices[unit] == NULL)
|
|
continue;
|
|
if (scglocal(scgp)->cam_devices[unit]->path_id == busno)
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_fileno(scgp, busno, unit, tlun)
|
|
SCSI *scgp;
|
|
int busno;
|
|
int unit;
|
|
int tlun;
|
|
{
|
|
int i;
|
|
|
|
if (scgp->local == NULL)
|
|
return (-1);
|
|
|
|
for (i = 0; scglocal(scgp)->cam_devices[i] != (struct cam_device *)-1; i++) {
|
|
if (scglocal(scgp)->cam_devices[i] == NULL)
|
|
continue;
|
|
if ((scglocal(scgp)->cam_devices[i]->path_id == busno) &&
|
|
(scglocal(scgp)->cam_devices[i]->target_id == unit) &&
|
|
(scglocal(scgp)->cam_devices[i]->target_lun == tlun))
|
|
return (i);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_initiator_id(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
return (-1);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_isatapi(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
return (FALSE);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_reset(scgp, what)
|
|
SCSI *scgp;
|
|
int what;
|
|
{
|
|
/* XXX synchronous reset command - is this wise? */
|
|
errno = EINVAL;
|
|
return (-1);
|
|
}
|
|
|
|
LOCAL int
|
|
scgo_send(scgp)
|
|
SCSI *scgp;
|
|
{
|
|
struct scg_cmd *sp = scgp->scmd;
|
|
struct cam_device *dev;
|
|
union ccb ccb_space;
|
|
union ccb *ccb = &ccb_space;
|
|
int rv, result;
|
|
u_int32_t flags;
|
|
int sense_len;
|
|
|
|
if (scgp->fd < 0) {
|
|
#if 0
|
|
js_fprintf((FILE *)scgp->errfile,
|
|
"attempt to reference invalid unit %d\n", scgp->fd);
|
|
#endif
|
|
sp->error = SCG_FATAL;
|
|
return (0);
|
|
}
|
|
|
|
dev = scglocal(scgp)->cam_devices[scgp->fd];
|
|
fillbytes(&ccb->ccb_h, sizeof (struct ccb_hdr), '\0');
|
|
ccb->ccb_h.path_id = dev->path_id;
|
|
ccb->ccb_h.target_id = dev->target_id;
|
|
ccb->ccb_h.target_lun = dev->target_lun;
|
|
|
|
/* Build the CCB */
|
|
fillbytes(&(&ccb->ccb_h)[1], sizeof (struct ccb_scsiio), '\0');
|
|
movebytes(sp->cdb.cmd_cdb, &ccb->csio.cdb_io.cdb_bytes, sp->cdb_len);
|
|
|
|
/*
|
|
* Set the data direction flags.
|
|
*/
|
|
if (sp->size != 0) {
|
|
flags = (sp->flags & SCG_RECV_DATA) ? CAM_DIR_IN :
|
|
CAM_DIR_OUT;
|
|
} else {
|
|
flags = CAM_DIR_NONE;
|
|
}
|
|
|
|
flags |= CAM_DEV_QFRZDIS;
|
|
|
|
/*
|
|
* If you don't want to bother with sending tagged commands under CAM,
|
|
* we don't need to do anything to cdrecord. If you want to send
|
|
* tagged commands to those devices that support it, we'll need to set
|
|
* the tag action valid field like this in scgo_send():
|
|
*
|
|
* flags |= CAM_DEV_QFRZDIS | CAM_TAG_ACTION_VALID;
|
|
*
|
|
* Note that SSD_FULL_SIZE is 32 on FreeBSD and that it is impossible
|
|
* to get more that SSD_FULL_SIZE == sizeof ((struct scsi_sense_data)
|
|
* on FreeBSD.
|
|
*/
|
|
sense_len = sp->sense_len;
|
|
if (sense_len > SSD_FULL_SIZE)
|
|
sense_len = SSD_FULL_SIZE;
|
|
|
|
cam_fill_csio(&ccb->csio,
|
|
/* retries */ 1,
|
|
/* cbfncp */ NULL,
|
|
/* flags */ flags,
|
|
/* tag_action */ MSG_SIMPLE_Q_TAG,
|
|
/* data_ptr */ (u_int8_t *)sp->addr,
|
|
/* dxfer_len */ sp->size,
|
|
/* sense_len */ sense_len,
|
|
/* cdb_len */ sp->cdb_len,
|
|
/* timeout */ sp->timeout * 1000);
|
|
|
|
/* Run the command */
|
|
errno = 0;
|
|
result = 16;
|
|
do {
|
|
/*
|
|
* CAM_REQUEUE_REQ is a very unspecified status code. For this
|
|
* reason, only the kernel could know why it happened.
|
|
* This is therefore a loop that should be handled inside the
|
|
* kernel in order not to break the layering model.
|
|
* FreBSD unfortunately has dozens of places in the kernel that
|
|
* set CAM_REQUEUE_REQ and most of the places that set
|
|
* CAM_REQUEUE_REQ are handling missing kernel resources. As
|
|
* it seems to be unlikely that there will be a clean fix in
|
|
* the kernel soon, we are forced to handle this status here.
|
|
*
|
|
* Note that for May 2010, there is at least one definite bug
|
|
* in the kernel where CAM_REQUEUE_REQ is returned for a SCSI
|
|
* reservation conflict.
|
|
*/
|
|
rv = cam_send_ccb(dev, ccb);
|
|
} while (rv != -1 && --result > 0 &&
|
|
(ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQUEUE_REQ);
|
|
|
|
if (rv == -1) {
|
|
return (rv);
|
|
} else {
|
|
/*
|
|
* Check for command status. Selection timeouts are fatal.
|
|
* For command timeouts, we pass back the appropriate
|
|
* error. If we completed successfully, there's (obviously)
|
|
* no error. We declare anything else "retryable".
|
|
*/
|
|
switch (ccb->ccb_h.status & CAM_STATUS_MASK) {
|
|
case CAM_SEL_TIMEOUT:
|
|
result = SCG_FATAL;
|
|
break;
|
|
case CAM_CMD_TIMEOUT:
|
|
result = SCG_TIMEOUT;
|
|
break;
|
|
case CAM_REQ_CMP:
|
|
result = SCG_NO_ERROR;
|
|
break;
|
|
default:
|
|
result = SCG_RETRYABLE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
sp->error = result;
|
|
if (result != SCG_NO_ERROR)
|
|
sp->ux_errno = EIO;
|
|
|
|
/* Pass the result back up */
|
|
fillbytes(&sp->scb, sizeof (sp->scb), '\0');
|
|
#ifdef CLEAR_SENSE
|
|
fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0');
|
|
#endif
|
|
sp->resid = ccb->csio.resid;
|
|
sp->sense_count = sense_len - ccb->csio.sense_resid;
|
|
|
|
/*
|
|
* Determine how much room we have for sense data in struct scg_cmd.
|
|
*/
|
|
if (sp->sense_count > SCG_MAX_SENSE)
|
|
sp->sense_count = SCG_MAX_SENSE;
|
|
|
|
/* Copy the sense data out */
|
|
movebytes(&ccb->csio.sense_data, &sp->u_sense.cmd_sense, sp->sense_count);
|
|
|
|
sp->u_scb.cmd_scb[0] = ccb->csio.scsi_status;
|
|
|
|
return (0);
|
|
}
|
|
|
|
#undef scsi_sense
|
|
#undef scsi_inquiry
|
|
#define sense u_sense.Sense
|
|
|
|
#endif /* BSD_CAM */
|