Logo Search packages:      
Sourcecode: cdrkit version File versions  Download package

scsi-sun.c

/*
 * 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 */

Generated by  Doxygen 1.6.0   Back to index