/*
* This file has been modified for the cdrkit suite.
*
* The behaviour and appearence of the program code below can differ to a major
* extent from the version distributed by the original author(s).
*
* For details, see Changelog file distributed with the cdrkit package. If you
* received this file from another source then ask the distributing person for
* a log of modifications.
*
*/
/* @(#)scsi-sun.c 1.83 05/11/20 Copyright 1988,1995,2000-2004 J. Schilling */
/*
* SCSI user level command transport routines for
* the SCSI general driver 'usal'.
*
* Warning: you may change this source, but if you do that
* you need to change the _usal_version and _usal_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) 1988,1995,2000-2004 J. Schilling
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; see the file COPYING. If not, write to the Free Software
* Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <usal/usalio.h>
#include <libport.h> /* Needed for gethostid() */
#ifdef HAVE_SUN_DKIO_H
# include <sun/dkio.h>
# define dk_cinfo dk_conf
# define dki_slave dkc_slave
# define DKIO_GETCINFO DKIOCGCONF
#endif
#ifdef HAVE_SYS_DKIO_H
# include <sys/dkio.h>
# define DKIO_GETCINFO DKIOCINFO
#endif
#define TARGET(slave) ((slave) >> 3)
#define LUN(slave) ((slave) & 07)
/*
* Tht USCSI ioctl() is not usable on SunOS 4.x
*/
#ifdef __SVR4
/*#define VOLMGT_DEBUG*/
#include <volmgt.h>
#include <statdefs.h>
# define USE_USCSI
#endif
static char _usal_trans_version[] = "usal-1.83"; /* The version for /dev/usal */
static char _usal_utrans_version[] = "uscsi-1.83"; /* The version for USCSI */
#ifdef USE_USCSI
static int usalo_uhelp(SCSI *usalp, FILE *f);
static int usalo_uopen(SCSI *usalp, char *device);
static int usalo_volopen(SCSI *usalp, char *devname);
static int usalo_openmedia(SCSI *usalp, char *mname);
static int usalo_uclose(SCSI *usalp);
static int usalo_ucinfo(int f, struct dk_cinfo *cp, int debug);
static int usalo_ugettlun(int f, int *tgtp, int *lunp);
static long usalo_umaxdma(SCSI *usalp, long amt);
static int usalo_openide(void);
static BOOL usalo_uhavebus(SCSI *usalp, int);
static int usalo_ufileno(SCSI *usalp, int, int, int);
static int usalo_uinitiator_id(SCSI *usalp);
static int usalo_uisatapi(SCSI *usalp);
static int usalo_ureset(SCSI *usalp, int what);
static int usalo_usend(SCSI *usalp);
static int have_volmgt = -1;
static usal_ops_t sun_uscsi_ops = {
usalo_usend,
usalo_version, /* Shared with SCG driver */
usalo_uhelp,
usalo_uopen,
usalo_uclose,
usalo_umaxdma,
usalo_getbuf, /* Shared with SCG driver */
usalo_freebuf, /* Shared with SCG driver */
usalo_uhavebus,
usalo_ufileno,
usalo_uinitiator_id,
usalo_uisatapi,
usalo_ureset,
};
#endif
/*
* Need to move this into an usal driver ioctl.
*/
/*#define MAX_DMA_SUN4M (1024*1024)*/
#define MAX_DMA_SUN4M (124*1024) /* Currently max working size */
/*#define MAX_DMA_SUN4C (126*1024)*/
#define MAX_DMA_SUN4C (124*1024) /* Currently max working size */
#define MAX_DMA_SUN3 (63*1024)
#define MAX_DMA_SUN386 (56*1024)
#define MAX_DMA_OTHER (32*1024)
#define ARCH_MASK 0xF0
#define ARCH_SUN2 0x00
#define ARCH_SUN3 0x10
#define ARCH_SUN4 0x20
#define ARCH_SUN386 0x30
#define ARCH_SUN3X 0x40
#define ARCH_SUN4C 0x50
#define ARCH_SUN4E 0x60
#define ARCH_SUN4M 0x70
#define ARCH_SUNX 0x80
/*
* We are using a "real" /dev/usal?
*/
#define scsi_xsend(usalp) ioctl((usalp)->fd, SCGIO_CMD, (usalp)->scmd)
#define MAX_SCG 16 /* Max # of SCSI controllers */
#define MAX_TGT 16
#define MAX_LUN 8
struct usal_local {
union {
int SCG_files[MAX_SCG];
#ifdef USE_USCSI
short usal_files[MAX_SCG][MAX_TGT][MAX_LUN];
#endif
} u;
};
#define usallocal(p) ((struct usal_local *)((p)->local))
#define usalfiles(p) (usallocal(p)->u.SCG_files)
/*
* Return version information for the low level SCSI transport code.
* This has been introduced to make it easier to trace down problems
* in applications.
*/
static char *
usalo_version(SCSI *usalp, int what)
{
if (usalp != (SCSI *)0) {
switch (what) {
case SCG_VERSION:
#ifdef USE_USCSI
if (usalp->ops == &sun_uscsi_ops)
return (_usal_utrans_version);
#endif
return (_usal_trans_version);
/*
* If you changed this source, you are not allowed to
* return "schily" for the SCG_AUTHOR request.
*/
case SCG_AUTHOR:
return (_usal_auth_cdrkit);
case SCG_SCCS_ID:
return (__sccsid);
}
}
return ((char *)0);
}
static int
usalo_help(SCSI *usalp, FILE *f)
{
__usal_help(f, "usal", "Generic transport independent SCSI",
"", "bus,target,lun", "1,2,0", TRUE, FALSE);
#ifdef USE_USCSI
usalo_uhelp(usalp, f);
#endif
return (0);
}
static int
usalo_open(SCSI *usalp, char *device)
{
int busno = usal_scsibus(usalp);
int tgt = usal_target(usalp);
/* int tlun = usal_lun(usalp);*/
register int f;
register int i;
register int nopen = 0;
char devname[32];
if (busno >= MAX_SCG) {
errno = EINVAL;
if (usalp->errstr)
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
"Illegal value for busno '%d'", busno);
return (-1);
}
if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) {
#ifdef USE_USCSI
usalp->ops = &sun_uscsi_ops;
return (SCGO_OPEN(usalp, device));
#else
errno = EINVAL;
if (usalp->errstr)
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
"Open by 'devname' not supported on this OS");
return (-1);
#endif
}
if (usalp->local == NULL) {
usalp->local = malloc(sizeof (struct usal_local));
if (usalp->local == NULL) {
if (usalp->errstr)
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "No memory for usal_local");
return (0);
}
for (i = 0; i < MAX_SCG; i++) {
usalfiles(usalp)[i] = -1;
}
}
for (i = 0; i < MAX_SCG; i++) {
/*
* Skip unneeded devices if not in SCSI Bus scan open mode
*/
if (busno >= 0 && busno != i)
continue;
snprintf(devname, sizeof (devname), "/dev/usal%d", i);
f = open(devname, O_RDWR);
if (f < 0) {
if (errno != ENOENT && errno != ENXIO) {
if (usalp->errstr)
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
"Cannot open '%s'", devname);
return (-1);
}
} else {
nopen++;
}
usalfiles(usalp)[i] = f;
}
#ifdef USE_USCSI
if (nopen <= 0) {
if (usalp->local != NULL) {
free(usalp->local);
usalp->local = NULL;
}
usalp->ops = &sun_uscsi_ops;
return (SCGO_OPEN(usalp, device));
}
#endif
return (nopen);
}
static int
usalo_close(SCSI *usalp)
{
register int i;
if (usalp->local == NULL)
return (-1);
for (i = 0; i < MAX_SCG; i++) {
if (usalfiles(usalp)[i] >= 0)
close(usalfiles(usalp)[i]);
usalfiles(usalp)[i] = -1;
}
return (0);
}
static long
usalo_maxdma(SCSI *usalp, long amt)
{
long maxdma = MAX_DMA_OTHER;
#if !defined(__i386_) && !defined(i386)
int cpu_type;
#endif
#if defined(__i386_) || defined(i386)
maxdma = MAX_DMA_SUN386;
#else
cpu_type = gethostid() >> 24;
switch (cpu_type & ARCH_MASK) {
case ARCH_SUN4C:
case ARCH_SUN4E:
maxdma = MAX_DMA_SUN4C;
break;
case ARCH_SUN4M:
case ARCH_SUNX:
maxdma = MAX_DMA_SUN4M;
break;
default:
maxdma = MAX_DMA_SUN3;
}
#endif
#ifndef __SVR4
/*
* SunOS 4.x allows esp hardware on VME boards and thus
* limits DMA on esp to 64k-1
*/
if (maxdma > MAX_DMA_SUN3)
maxdma = MAX_DMA_SUN3;
#endif
return (maxdma);
}
static BOOL
usalo_havebus(SCSI *usalp, int busno)
{
if (usalp->local == NULL)
return (FALSE);
return (busno < 0 || busno >= MAX_SCG) ? FALSE : (usalfiles(usalp)[busno] >= 0);
}
static int
usalo_fileno(SCSI *usalp, int busno, int tgt, int tlun)
{
if (usalp->local == NULL)
return (-1);
return ((busno < 0 || busno >= MAX_SCG) ? -1 : usalfiles(usalp)[busno]);
}
static int
usalo_initiator_id(SCSI *usalp)
{
int id = -1;
#ifdef DKIO_GETCINFO
struct dk_cinfo conf;
#endif
#ifdef DKIO_GETCINFO
if (usalp->fd < 0)
return (id);
if (ioctl(usalp->fd, DKIO_GETCINFO, &conf) < 0)
return (id);
if (TARGET(conf.dki_slave) != -1)
id = TARGET(conf.dki_slave);
#endif
return (id);
}
static int
usalo_isatapi(SCSI *usalp)
{
return (FALSE);
}
static int
usalo_reset(SCSI *usalp, int what)
{
if (what == SCG_RESET_NOP)
return (0);
if (what != SCG_RESET_BUS) {
errno = EINVAL;
return (-1);
}
return (ioctl(usalp->fd, SCGIORESET, 0));
}
static void *
usalo_getbuf(SCSI *usalp, long amt)
{
usalp->bufbase = (void *)valloc((size_t)amt);
return (usalp->bufbase);
}
static void
usalo_freebuf(SCSI *usalp)
{
if (usalp->bufbase)
free(usalp->bufbase);
usalp->bufbase = NULL;
}
static int
usalo_send(SCSI *usalp)
{
usalp->scmd->target = usal_target(usalp);
return (ioctl(usalp->fd, SCGIO_CMD, usalp->scmd));
}
/*--------------------------------------------------------------------------*/
/*
* This is Sun USCSI interface code ...
*/
#ifdef USE_USCSI
#include <sys/scsi/impl/uscsi.h>
/*
* Bit Mask definitions, for use accessing the status as a byte.
*/
#define STATUS_MASK 0x3E
#define STATUS_GOOD 0x00
#define STATUS_CHECK 0x02
#define STATUS_RESERVATION_CONFLICT 0x18
#define STATUS_TERMINATED 0x22
#ifdef nonono
#define STATUS_MASK 0x3E
#define STATUS_GOOD 0x00
#define STATUS_CHECK 0x02
#define STATUS_MET 0x04
#define STATUS_BUSY 0x08
#define STATUS_INTERMEDIATE 0x10
#define STATUS_SCSI2 0x20
#define STATUS_INTERMEDIATE_MET 0x14
#define STATUS_RESERVATION_CONFLICT 0x18
#define STATUS_TERMINATED 0x22
#define STATUS_QFULL 0x28
#define STATUS_ACA_ACTIVE 0x30
#endif
static int
usalo_uhelp(SCSI *usalp, FILE *f)
{
__usal_help(f, "USCSI", "SCSI transport for targets known by Solaris drivers",
"USCSI:", "bus,target,lun", "USCSI:1,2,0", TRUE, TRUE);
return (0);
}
static int
usalo_uopen(SCSI *usalp, char *device)
{
int busno = usal_scsibus(usalp);
int tgt = usal_target(usalp);
int tlun = usal_lun(usalp);
register int f;
register int b;
register int t;
register int l;
register int nopen = 0;
char devname[32];
if (have_volmgt < 0)
have_volmgt = volmgt_running();
if (usalp->overbose) {
fprintf((FILE *)usalp->errfile,
"Warning: Using USCSI interface.\n");
}
#ifndef SEEK_HOLE
/*
* SEEK_HOLE first appears in Solaris 11 Build 14, volmgt supports
* medialess drives since Build 21. Using SEEK_HOLD as indicator
* seems to be the best guess.
*/
if (usalp->overbose > 0 && have_volmgt) {
fprintf((FILE *)usalp->errfile,
"Warning: Volume management is running, medialess managed drives are invisible.\n");
}
#endif
if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) {
errno = EINVAL;
if (usalp->errstr) {
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE,
"Illegal value for busno, target or lun '%d,%d,%d'",
busno, tgt, tlun);
}
return (-1);
}
if (usalp->local == NULL) {
usalp->local = malloc(sizeof (struct usal_local));
if (usalp->local == NULL) {
if (usalp->errstr)
snprintf(usalp->errstr, SCSI_ERRSTR_SIZE, "No memory for usal_local");
return (0);
}
for (b = 0; b < MAX_SCG; b++) {
for (t = 0; t < MAX_TGT; t++) {
for (l = 0; l < MAX_LUN; l++)
usallocal(usalp)->u.usal_files[b][t][l] = (short)-1;
}
}
}
if (device != NULL && strcmp(device, "USCSI") == 0)
goto uscsiscan;
if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2))
goto openbydev;
uscsiscan:
if (busno >= 0 && tgt >= 0 && tlun >= 0) {
if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN)
return (-1);
snprintf(devname, sizeof (devname), "/dev/rdsk/c%dt%dd%ds2",
busno, tgt, tlun);
f = open(devname, O_RDONLY | O_NDELAY);
if (f < 0 && geterrno() == EBUSY)
f = usalo_volopen(usalp, devname);
if (f < 0) {
snprintf(usalp->errstr,
SCSI_ERRSTR_SIZE,
"Cannot open '%s'", devname);
return (0);
}
usallocal(usalp)->u.usal_files[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++) {
snprintf(devname, sizeof (devname),
"/dev/rdsk/c%dt%dd%ds2",
b, t, l);
f = open(devname, O_RDONLY | O_NDELAY);
if (f < 0 && geterrno() == EBUSY) {
f = usalo_volopen(usalp, devname);
/*
* Hack to mark inaccessible
* drives with fd == -2
*/
if (f < 0 &&
usallocal(usalp)->u.usal_files[b][t][l] < 0)
usallocal(usalp)->u.usal_files[b][t][l] = f;
}
if (f < 0 && errno != ENOENT &&
errno != ENXIO &&
errno != ENODEV) {
if (usalp->errstr)
snprintf(usalp->errstr,
SCSI_ERRSTR_SIZE,
"Cannot open '%s'", devname);
}
if (f < 0 && l == 0)
break;
if (f >= 0) {
nopen ++;
if (usallocal(usalp)->u.usal_files[b][t][l] == -1)
usallocal(usalp)->u.usal_files[b][t][l] = f;
else
close(f);
}
}
}
}
}
openbydev:
if (nopen == 0) {
int target;
int lun;
if (device != NULL && strncmp(device, "USCSI:", 6) == 0)
device += 6;
if (device == NULL || device[0] == '\0')
return (0);
f = open(device, O_RDONLY | O_NDELAY);
if (f < 0)
f = usalo_volopen(usalp, device);
if (f < 0) {
snprintf(usalp->errstr,
SCSI_ERRSTR_SIZE,
"Cannot open '%s'", device);
return (0);
}
if (busno < 0)
busno = 0; /* Use Fake number if not specified */
if (usalo_ugettlun(f, &target, &lun) >= 0) {
if (tgt >= 0 && tlun >= 0) {
if (tgt != target || tlun != lun) {
close(f);
return (0);
}
}
tgt = target;
tlun = lun;
} else {
if (tgt < 0 || tlun < 0) {
close(f);
return (0);
}
}
if (usallocal(usalp)->u.usal_files[busno][tgt][tlun] == -1)
usallocal(usalp)->u.usal_files[busno][tgt][tlun] = f;
usal_settarget(usalp, busno, tgt, tlun);
return (++nopen);
}
return (nopen);
}
static int
usalo_volopen(SCSI *usalp, char *devname)
{
int oerr = geterrno();
int f = -1;
char *name = NULL; /* Volume symbolic device name */
char *symdev = NULL; /* /dev/... name based on "name" */
char *mname = NULL; /* Volume media name based on "name" */
if (!have_volmgt)
return (-1);
#ifdef VOLMGT_DEBUG
usalp->debug++;
#endif
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"usalo_volopen(%s)\n", devname);
}
/*
* We come here because trying to open "devname" did not work.
* First try to translate between a symbolic name and a /dev/...
* based device name. Then translate back to a symbolic name.
*/
symdev = volmgt_symdev(devname);
if (symdev)
name = volmgt_symname(symdev);
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"volmgt_symdev(%s)=%s -> %s\n", devname, symdev, name);
}
/*
* If "devname" is not a symbolic device name, then it must be
* a /dev/... based device name. Try to translate it into a
* symbolic name. Then translate back to a /dev/... name.
*/
if (name == NULL) {
name = volmgt_symname(devname);
if (name)
symdev = volmgt_symdev(name);
}
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"volmgt_symdev(%s)=%s -> %s\n", devname, symdev, name);
}
/*
* If we have been able to translate to a symbolic device name,
* translate this name into a volume management media name that
* may be used for opening.
*/
if (name)
mname = media_findname(name);
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"symdev %s name %s mname %s\n", symdev, name, mname);
}
/*
* Das scheint nur mit dem normierten /dev/rdsk/ *s2 Namen zu gehen.
*/
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"volmgt_inuse(%s) %d\n", symdev, volmgt_inuse(symdev));
}
if (mname)
f = usalo_openmedia(usalp, mname);
else if (name)
f = -2; /* Mark inaccessible drives with fd == -2 */
/*
* Besonderen Fehlertext oder fprintf/errfile bei non-scanbus Open und
* wenn errrno == EBUSY && kein Mapping?
*/
if (name)
free(name);
if (symdev)
free(symdev);
if (mname)
free(mname);
seterrno(oerr);
#ifdef VOLMGT_DEBUG
usalp->debug--;
#endif
return (f);
}
static int
usalo_openmedia(SCSI *usalp, char *mname)
{
int f = -1;
char *device = NULL;
struct stat sb;
if (mname == NULL)
return (-1);
/*
* Check whether the media name refers to a directory.
* In this case, the medium is partitioned and we need to
* check all partitions.
*/
if (stat(mname, &sb) >= 0) {
if (S_ISDIR(sb.st_mode)) {
char name[128];
int i;
/*
* First check partition '2', the whole disk.
*/
snprintf(name, sizeof (name), "%s/s2", mname);
f = open(name, O_RDONLY | O_NDELAY);
if (f >= 0)
return (f);
/*
* Now try all other partitions.
*/
for (i = 0; i < 16; i++) {
if (i == 2)
continue;
snprintf(name, sizeof (name),
"%s/s%d", mname, i);
if (stat(name, &sb) >= 0)
break;
}
if (i < 16) {
device = mname;
}
} else {
device = mname;
}
}
if (device)
f = open(device, O_RDONLY | O_NDELAY);
return (f);
}
static int
usalo_uclose(SCSI *usalp)
{
register int f;
register int b;
register int t;
register int l;
if (usalp->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 = usallocal(usalp)->u.usal_files[b][t][l];
if (f >= 0)
close(f);
usallocal(usalp)->u.usal_files[b][t][l] = (short)-1;
}
}
}
return (0);
}
static int
usalo_ucinfo(int f, struct dk_cinfo *cp, int debug)
{
fillbytes(cp, sizeof (*cp), '\0');
if (ioctl(f, DKIOCINFO, cp) < 0)
return (-1);
if (debug <= 0)
return (0);
fprintf(stderr, "cname: '%s'\n", cp->dki_cname);
fprintf(stderr, "ctype: 0x%04hX %hd\n", cp->dki_ctype, cp->dki_ctype);
fprintf(stderr, "cflags: 0x%04hX\n", cp->dki_flags);
fprintf(stderr, "cnum: %hd\n", cp->dki_cnum);
#ifdef __EVER__
fprintf(stderr, "addr: %d\n", cp->dki_addr);
fprintf(stderr, "space: %d\n", cp->dki_space);
fprintf(stderr, "prio: %d\n", cp->dki_prio);
fprintf(stderr, "vec: %d\n", cp->dki_vec);
#endif
fprintf(stderr, "dname: '%s'\n", cp->dki_dname);
fprintf(stderr, "unit: %d\n", cp->dki_unit);
fprintf(stderr, "slave: %d %04o Tgt: %d Lun: %d\n",
cp->dki_slave, cp->dki_slave,
TARGET(cp->dki_slave), LUN(cp->dki_slave));
fprintf(stderr, "partition: %hd\n", cp->dki_partition);
fprintf(stderr, "maxtransfer: %d (%d)\n",
cp->dki_maxtransfer,
cp->dki_maxtransfer * DEV_BSIZE);
return (0);
}
static int
usalo_ugettlun(int f, int *tgtp, int *lunp)
{
struct dk_cinfo ci;
if (usalo_ucinfo(f, &ci, 0) < 0)
return (-1);
if (tgtp)
*tgtp = TARGET(ci.dki_slave);
if (lunp)
*lunp = LUN(ci.dki_slave);
return (0);
}
static long
usalo_umaxdma(SCSI *usalp, long amt)
{
register int b;
register int t;
register int l;
long maxdma = -1L;
int f;
struct dk_cinfo ci;
BOOL found_ide = FALSE;
if (usalp->local == NULL)
return (-1L);
for (b = 0; b < MAX_SCG; b++) {
for (t = 0; t < MAX_TGT; t++) {
for (l = 0; l < MAX_LUN; l++) {
if ((f = usallocal(usalp)->u.usal_files[b][t][l]) < 0)
continue;
if (usalo_ucinfo(f, &ci, usalp->debug) < 0)
continue;
if (maxdma < 0)
maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
if (maxdma > (long)(ci.dki_maxtransfer * DEV_BSIZE))
maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
if (streql(ci.dki_cname, "ide"))
found_ide = TRUE;
}
}
}
#if defined(__i386_) || defined(i386)
/*
* At least on Solaris 9 x86, DKIOCINFO returns a wrong value
* for dki_maxtransfer if the target is an ATAPI drive.
* Without DMA, it seems to work if we use 256 kB DMA size for ATAPI,
* but if we allow DMA, only 68 kB will work (for more we get a silent
* DMA residual count == DMA transfer count).
* For this reason, we try to figure out the correct value for 'ide'
* by retrieving the (correct) value from a ide hard disk.
*/
if (found_ide) {
if ((f = usalo_openide()) >= 0) {
#ifdef sould_we
long omaxdma = maxdma;
#endif
if (usalo_ucinfo(f, &ci, usalp->debug) >= 0) {
if (maxdma < 0)
maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
if (maxdma > (long)(ci.dki_maxtransfer * DEV_BSIZE))
maxdma = (long)(ci.dki_maxtransfer * DEV_BSIZE);
}
close(f);
#ifdef sould_we
/*
* The kernel returns 56 kB but we tested that 68 kB works.
*/
if (omaxdma > maxdma && maxdma == (112 * DEV_BSIZE))
maxdma = 136 * DEV_BSIZE;
#endif
} else {
/*
* No IDE disk on this system?
*/
if (maxdma == (512 * DEV_BSIZE))
maxdma = MAX_DMA_SUN386;
}
}
#endif
/*
* The Sun tape driver does not support to retrieve the max DMA count.
* Use the knwoledge about default DMA sizes in this case.
*/
if (maxdma < 0)
maxdma = usalo_maxdma(usalp, amt);
return (maxdma);
}
#if defined(__i386_) || defined(i386)
static int
usalo_openide()
{
char buf[20];
int b;
int t;
int f = -1;
for (b = 0; b < 5; b++) {
for (t = 0; t < 2; t++) {
snprintf(buf, sizeof (buf),
"/dev/rdsk/c%dd%dp0", b, t);
if ((f = open(buf, O_RDONLY | O_NDELAY)) >= 0)
goto out;
}
}
out:
return (f);
}
#endif
static BOOL
usalo_uhavebus(SCSI *usalp, int busno)
{
register int t;
register int l;
if (usalp->local == NULL || busno < 0 || busno >= MAX_SCG)
return (FALSE);
for (t = 0; t < MAX_TGT; t++) {
for (l = 0; l < MAX_LUN; l++)
if (usallocal(usalp)->u.usal_files[busno][t][l] >= 0)
return (TRUE);
}
return (FALSE);
}
static int
usalo_ufileno(SCSI *usalp, int busno, int tgt, int tlun)
{
if (usalp->local == NULL ||
busno < 0 || busno >= MAX_SCG ||
tgt < 0 || tgt >= MAX_TGT ||
tlun < 0 || tlun >= MAX_LUN)
return (-1);
return ((int)usallocal(usalp)->u.usal_files[busno][tgt][tlun]);
}
static int
usalo_uinitiator_id(SCSI *usalp)
{
return (-1);
}
static int
usalo_uisatapi(SCSI *usalp)
{
char devname[32];
char symlinkname[MAXPATHLEN];
int len;
struct dk_cinfo ci;
if (ioctl(usalp->fd, DKIOCINFO, &ci) < 0)
return (-1);
snprintf(devname, sizeof (devname), "/dev/rdsk/c%dt%dd%ds2",
usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
symlinkname[0] = '\0';
len = readlink(devname, symlinkname, sizeof (symlinkname));
if (len > 0)
symlinkname[len] = '\0';
if (len >= 0 && strstr(symlinkname, "ide") != NULL)
return (TRUE);
else
return (FALSE);
}
static int
usalo_ureset(SCSI *usalp, int what)
{
struct uscsi_cmd req;
if (what == SCG_RESET_NOP)
return (0);
fillbytes(&req, sizeof (req), '\0');
if (what == SCG_RESET_TGT) {
req.uscsi_flags = USCSI_RESET | USCSI_SILENT; /* reset target */
} else if (what != SCG_RESET_BUS) {
req.uscsi_flags = USCSI_RESET_ALL | USCSI_SILENT; /* reset bus */
} else {
errno = EINVAL;
return (-1);
}
return (ioctl(usalp->fd, USCSICMD, &req));
}
static int
usalo_usend(SCSI *usalp)
{
struct usal_cmd *sp = usalp->scmd;
struct uscsi_cmd req;
int ret;
static uid_t cureuid = 0; /* XXX Hack until we have uid management */
if (usalp->fd < 0) {
sp->error = SCG_FATAL;
return (0);
}
fillbytes(&req, sizeof (req), '\0');
req.uscsi_flags = USCSI_SILENT | USCSI_DIAGNOSE | USCSI_RQENABLE;
if (sp->flags & SCG_RECV_DATA) {
req.uscsi_flags |= USCSI_READ;
} else if (sp->size > 0) {
req.uscsi_flags |= USCSI_WRITE;
}
req.uscsi_buflen = sp->size;
req.uscsi_bufaddr = sp->addr;
req.uscsi_timeout = sp->timeout;
req.uscsi_cdblen = sp->cdb_len;
req.uscsi_rqbuf = (caddr_t) sp->u_sense.cmd_sense;
req.uscsi_rqlen = sp->sense_len;
req.uscsi_cdb = (caddr_t) &sp->cdb;
if (cureuid != 0)
seteuid(0);
again:
errno = 0;
ret = ioctl(usalp->fd, USCSICMD, &req);
if (ret < 0 && geterrno() == EPERM) { /* XXX Hack until we have uid management */
cureuid = geteuid();
if (seteuid(0) >= 0)
goto again;
}
if (cureuid != 0)
seteuid(cureuid);
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile, "ret: %d errno: %d (%s)\n", ret, errno, errmsgstr(errno));
fprintf((FILE *)usalp->errfile, "uscsi_flags: 0x%x\n", req.uscsi_flags);
fprintf((FILE *)usalp->errfile, "uscsi_status: 0x%x\n", req.uscsi_status);
fprintf((FILE *)usalp->errfile, "uscsi_timeout: %d\n", req.uscsi_timeout);
fprintf((FILE *)usalp->errfile, "uscsi_bufaddr: 0x%lx\n", (long)req.uscsi_bufaddr);
/*
* Cast auf int OK solange sp->size
* auch ein int bleibt.
*/
fprintf((FILE *)usalp->errfile, "uscsi_buflen: %d\n", (int)req.uscsi_buflen);
fprintf((FILE *)usalp->errfile, "uscsi_resid: %d\n", (int)req.uscsi_resid);
fprintf((FILE *)usalp->errfile, "uscsi_rqlen: %d\n", req.uscsi_rqlen);
fprintf((FILE *)usalp->errfile, "uscsi_rqstatus: 0x%x\n", req.uscsi_rqstatus);
fprintf((FILE *)usalp->errfile, "uscsi_rqresid: %d\n", req.uscsi_rqresid);
fprintf((FILE *)usalp->errfile, "uscsi_rqbuf ptr: 0x%lx\n", (long)req.uscsi_rqbuf);
fprintf((FILE *)usalp->errfile, "uscsi_rqbuf: ");
if (req.uscsi_rqbuf != NULL && req.uscsi_rqlen > req.uscsi_rqresid) {
int i;
int len = req.uscsi_rqlen - req.uscsi_rqresid;
for (i = 0; i < len; i++) {
fprintf((FILE *)usalp->errfile, "0x%02X ", ((char *)req.uscsi_rqbuf)[i]);
}
fprintf((FILE *)usalp->errfile, "\n");
} else {
fprintf((FILE *)usalp->errfile, "<data not available>\n");
}
}
if (ret < 0) {
sp->ux_errno = geterrno();
/*
* Check if SCSI command cound not be send at all.
*/
if (sp->ux_errno == ENOTTY && usalo_uisatapi(usalp) == TRUE) {
if (usalp->debug > 0) {
fprintf((FILE *)usalp->errfile,
"ENOTTY atapi: %d\n", usalo_uisatapi(usalp));
}
sp->error = SCG_FATAL;
return (0);
}
if (errno == ENXIO) {
sp->error = SCG_FATAL;
return (0);
}
if (errno == ENOTTY || errno == EINVAL || errno == EACCES) {
return (-1);
}
} else {
sp->ux_errno = 0;
}
ret = 0;
sp->sense_count = req.uscsi_rqlen - req.uscsi_rqresid;
sp->resid = req.uscsi_resid;
sp->u_scb.cmd_scb[0] = req.uscsi_status;
if ((req.uscsi_status & STATUS_MASK) == STATUS_GOOD) {
sp->error = SCG_NO_ERROR;
return (0);
}
if (req.uscsi_rqstatus == 0 &&
((req.uscsi_status & STATUS_MASK) == STATUS_CHECK)) {
sp->error = SCG_NO_ERROR;
return (0);
}
if (req.uscsi_status & (STATUS_TERMINATED |
STATUS_RESERVATION_CONFLICT)) {
sp->error = SCG_FATAL;
}
if (req.uscsi_status != 0) {
/*
* This is most likely wrong. There seems to be no way
* to produce SCG_RETRYABLE with USCSI.
*/
sp->error = SCG_RETRYABLE;
}
return (ret);
}
#endif /* USE_USCSI */