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

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

/* @(#)scsiopen.c 1.95 04/01/14 Copyright 1995,2000 J. Schilling */
/*
 *    SCSI command functions for cdrecord
 *
 *    Copyright (c) 1995,2000 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.
 */

/*
 * NOTICE:  The Philips CDD 521 has several firmware bugs.
 *          One of them is not to respond to a SCSI selection
 *          within 200ms if the general load on the
 *          SCSI bus is high. To deal with this problem
 *          most of the SCSI commands are send with the
 *          SCG_CMD_RETRY flag enabled.
 *
 *          Note that the only legal place to assign
 *          values to usal_scsibus() usal_target() and usal_lun()
 *          is usal_settarget().
 */
#include <mconfig.h>

#include <stdio.h>
#include <standard.h>
#include <stdxlib.h>
#include <unixstd.h>
#include <fctldefs.h>
#include <errno.h>
#include <strdefs.h>
#include <timedefs.h>

#include <utypes.h>
#include <btorder.h>
#include <schily.h>

#include <usal/usalcmd.h>
#include <usal/scsidefs.h>
#include <usal/scsireg.h>
#include <usal/scsitransp.h>

#if    defined(linux) || defined(__linux) || defined(__linux__)
extern BOOL check_linux_26();
#endif

#define     strbeg(s1, s2)    (strstr((s2), (s1)) == (s2))

extern      int   lverbose;

SCSI  *usal_open(char *scsidev, char *errs, int slen, int debug, int be_verbose);
int   usal_help(FILE *f);
static      int   usal_scandev(char *devp, char *errs, int slen, int *busp, 
                                                      int *tgtp, int *lunp);
int   usal_close(SCSI * usalp);

void  usal_settimeout(SCSI * usalp, int timeout);

SCSI  *usal_smalloc(void);
void  usal_sfree(SCSI *usalp);

/*
 * Open a SCSI device.
 *
 * Possible syntax is:
 *
 * Preferred:
 *    dev=target,lun / dev=scsibus,target,lun
 *
 * Needed on some systems:
 *    dev=devicename:target,lun / dev=devicename:scsibus,target,lun
 *
 * On systems that don't support SCSI Bus scanning this syntax helps:
 *    dev=devicename:@ / dev=devicename:@,lun
 * or dev=devicename (undocumented)
 *
 * NOTE: As the 'lun' is part of the SCSI command descriptor block, it
 *     must always be known. If the OS cannot map it, it must be
 *     specified on command line.
 */
SCSI *
usal_open(char *scsidev, char *errs, int slen, int debug, int be_verbose)
{
      char  devname[256];
      char  *devp = NULL;
      char  *sdev = NULL;
      int   x1;
      int   bus = 0;
      int   tgt = 0;
      int   lun = 0;
      int   n = 0;
      SCSI  *usalp;

      if (errs)
            errs[0] = '\0';
      usalp = usal_smalloc();
      if (usalp == NULL) {
            if (errs)
                  snprintf(errs, slen, "No memory for SCSI structure");
            return ((SCSI *)0);
      }
      usalp->debug = debug;
      usalp->overbose = be_verbose;
      devname[0] = '\0';
      if (scsidev != NULL && scsidev[0] != '\0') {
            sdev = scsidev;
            if ((strncmp(scsidev, "HELP", 4) == 0) ||
                (strncmp(scsidev, "help", 4) == 0)) {

                  return ((SCSI *)0);
            }
            if (strncmp(scsidev, "REMOTE", 6) == 0) {
                  /*
                   * REMOTE:user@host:scsidev or
                   * REMOTE(transp):user@host:scsidev
                   * e.g.: REMOTE(/usr/bin/ssh):user@host:scsidev
                   *
                   * We must send the complete device spec to the remote
                   * site to allow parsing on both sites.
                   */
                  strncpy(devname, scsidev, sizeof (devname)-1);
                  devname[sizeof (devname)-1] = '\0';
                  if (sdev[6] == '(' || sdev[6] == ':')
                        sdev = strchr(sdev, ':');
                  else
                        sdev = NULL;

                  if (sdev == NULL) {
                        /*
                         * This seems to be an illegal remote dev spec.
                         * Give it a chance with a standard parsing.
                         */
                        sdev = scsidev;
                        devname[0] = '\0';
                  } else {
                        /*
                         * Now try to go past user@host spec.
                         */
                        if (sdev)
                              sdev = strchr(&sdev[1], ':');
                        if (sdev)
                              sdev++;     /* Device name follows ... */
                        else
                              goto nulldevice;
                  }
            }
            if ((devp = strchr(sdev, ':')) == NULL) {
                  if (strchr(sdev, ',') == NULL) {
                        /* Notation form: 'devname' (undocumented)  */
                        /* Forward complete name to usal__open()      */
                        /* Fetch bus/tgt/lun values from OS     */
                        /* We may come here too with 'USCSI'          */
                        n = -1;
                        lun  = -2;  /* Lun must be known        */
                        if (devname[0] == '\0') {
                              strncpy(devname, scsidev,
                                          sizeof (devname)-1);
                              devname[sizeof (devname)-1] = '\0';
                        }
                  } else {
                        /* Basic notation form: 'bus,tgt,lun'         */
                        devp = sdev;
                  }
            } else {
                  /* Notation form: 'devname:bus,tgt,lun'/'devname:@' */
                  /* We may come here too with 'USCSI:'               */
                  if (devname[0] == '\0') {
                        /*
                         * Copy over the part before the ':'
                         */
                        x1 = devp - scsidev;
                        if (x1 >= (int)sizeof (devname))
                              x1 = sizeof (devname)-1;
                        strncpy(devname, scsidev, x1);
                        devname[x1] = '\0';
                  }
                  devp++;
                  /* Check for a notation in the form 'devname:@'     */
                  if (devp[0] == '@') {
                        if (devp[1] == '\0') {
                              lun = -2;
                        } else if (devp[1] == ',') {
                              if (*astoi(&devp[2], &lun) != '\0') {
                                    errno = EINVAL;
                                    if (errs)
                                          snprintf(errs, slen,
                                                "Invalid lun specifier '%s'",
                                                            &devp[2]);
                                    return ((SCSI *)0);
                              }
                        }
                        n = -1;
                        /*
                         * Got device:@ or device:@,lun
                         * Make sure not to call usal_scandev()
                         */
                        devp = NULL;
                  } else if (devp[0] == '\0') {
                        /*
                         * Got USCSI: or ATAPI:
                         * Make sure not to call usal_scandev()
                         */
                        devp = NULL;
                  } else if (strchr(sdev, ',') == NULL) {
                        /* We may come here with 'ATAPI:/dev/hdc'   */
                        strncpy(devname, scsidev,
                                    sizeof (devname)-1);
                        devname[sizeof (devname)-1] = '\0';
                        n = -1;
                        lun  = -2;  /* Lun must be known        */
                        /*
                         * Make sure not to call usal_scandev()
                         */
                        devp = NULL;
                  }
            }
      }
nulldevice:

/*fprintf(stderr, "10 scsidev '%s' sdev '%s' devp '%s' b: %d t: %d l: %d\n", scsidev, sdev, devp, bus, tgt, lun);*/

      if (devp != NULL) {
            n = usal_scandev(devp, errs, slen, &bus, &tgt, &lun);
            if (n < 0) {
                  errno = EINVAL;
                  return ((SCSI *)0);
            }
      }
      if (n >= 1 && n <= 3) { /* Got bus,target,lun or target,lun or tgt*/
            usal_settarget(usalp, bus, tgt, lun);
      } else if (n == -1) {   /* Got device:@, fetch bus/lun from OS    */
            usal_settarget(usalp, -2, -2, lun);
      } else if (devp != NULL) {
            /*
             * XXX May this happen after we allow tgt to repesent tgt,0 ?
             */
            fprintf(stderr, "WARNING: device not valid, trying to use default target...\n");
            usal_settarget(usalp, 0, 6, 0);
      }
      if (be_verbose && scsidev != NULL) {
            fprintf(stderr, "scsidev: '%s'\n", scsidev);
            if (devname[0] != '\0')
                  fprintf(stderr, "devname: '%s'\n", devname);
            fprintf(stderr, "scsibus: %d target: %d lun: %d\n",
                              usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
      }
      if (debug > 0) {
            fprintf(stderr, "usal__open(%s) %d,%d,%d\n",
                  devname,
                  usal_scsibus(usalp), usal_target(usalp), usal_lun(usalp));
      }
      if (usal__open(usalp, devname) <= 0) {
            if (errs && usalp->errstr)
                  snprintf(errs, slen, "%s", usalp->errstr);
            usal_sfree(usalp);
            return ((SCSI *)0);
      }
      return (usalp);
}

int
usal_help(FILE *f)
{
      SCSI  *usalp;

      usalp = usal_smalloc();
      if (usalp != NULL) {
extern      usal_ops_t usal_std_ops;

            usalp->ops = &usal_std_ops;

            printf("Supported SCSI transports for this platform:\n");
            SCGO_HELP(usalp, f);
            usal_remote()->usalo_help(usalp, f);
            usal_sfree(usalp);
      }
      return (0);
}

/*
 * Convert target,lun or scsibus,target,lun syntax.
 * Check for bad syntax and invalid values.
 * This is definitely better than using scanf() as it checks for syntax errors.
 */
static int
usal_scandev(char *devp, char *errs, int slen, int *busp, int *tgtp, int *lunp)
{
      int   x1, x2, x3;
      int   n = 0;
      char  *p = devp;

      x1 = x2 = x3 = 0;
      *busp = *tgtp = *lunp = 0;

      if (*p != '\0') {
            p = astoi(p, &x1);
            if (*p == ',') {
                  p++;
                  n++;
            } else {
                  if (errs)
                        snprintf(errs, slen, "Invalid bus or target specifier in '%s'", devp);
                  return (-1);
            }
      }
      if (*p != '\0') {
            p = astoi(p, &x2);
            if (*p == ',' || *p == '\0') {
                  if (*p != '\0')
                        p++;
                  n++;
            } else {
                  if (errs)
                        snprintf(errs, slen, "Invalid target or lun specifier in '%s'", devp);
                  return (-1);
            }
      }
      if (*p != '\0') {
            p = astoi(p, &x3);
            if (*p == '\0') {
                  n++;
            } else {
                  if (errs)
                        snprintf(errs, slen, "Invalid lun specifier in '%s'", devp);
                  return (-1);
            }
      }
      if (n == 3) {
            *busp = x1;
            *tgtp = x2;
            *lunp = x3;
      }
      if (n == 2) {
            *tgtp = x1;
            *lunp = x2;
      }
      if (n == 1) {
            *tgtp = x1;
      }

      if (x1 < 0 || x2 < 0 || x3 < 0) {
            if (errs)
                  snprintf(errs, slen, "Invalid value for bus, target or lun (%d,%d,%d)",
                        *busp, *tgtp, *lunp);
            return (-1);
      }
      return (n);
}

int
usal_close(SCSI *usalp)
{
      usal__close(usalp);
      usal_sfree(usalp);
      return (0);
}

char * usal_natname(SCSI *usalp, int busno, int tgt, int tlun) {
      return usalp->ops->usalo_natname(usalp, busno, tgt, tlun);
}

int usal_fileno(SCSI *usalp, int busno, int tgt, int tlun) {
      return usalp->ops->usalo_fileno(usalp, busno, tgt, tlun);
}

void
usal_settimeout(SCSI *usalp, int timeout)
{
#ifdef      nonono
      if (timeout >= 0)
            usalp->deftimeout = timeout;
#else
      usalp->deftimeout = timeout;
#endif
}

SCSI *
usal_smalloc()
{
      SCSI  *usalp;
extern      usal_ops_t usal_dummy_ops;

      usalp = (SCSI *)malloc(sizeof (*usalp));
      if (usalp == NULL)
            return ((SCSI *)0);

      fillbytes(usalp, sizeof (*usalp), 0);
      usalp->ops  = &usal_dummy_ops;
      usal_settarget(usalp, -1, -1, -1);
      usalp->fd   = -1;
      usalp->deftimeout = 20;
      usalp->running    = FALSE;

      usalp->cmdstart = (struct timeval *)malloc(sizeof (struct timeval));
      if (usalp->cmdstart == NULL)
            goto err;
      usalp->cmdstop = (struct timeval *)malloc(sizeof (struct timeval));
      if (usalp->cmdstop == NULL)
            goto err;
      usalp->scmd = (struct usal_cmd *)malloc(sizeof (struct usal_cmd));
      if (usalp->scmd == NULL)
            goto err;
      usalp->errstr = malloc(SCSI_ERRSTR_SIZE);
      if (usalp->errstr == NULL)
            goto err;
      usalp->errptr = usalp->errbeg = usalp->errstr;
      usalp->errstr[0] = '\0';
      usalp->errfile = (void *)stderr;
      usalp->inq = (struct scsi_inquiry *)malloc(sizeof (struct scsi_inquiry));
      if (usalp->inq == NULL)
            goto err;
      usalp->cap = (struct scsi_capacity *)malloc(sizeof (struct scsi_capacity));
      if (usalp->cap == NULL)
            goto err;

      return (usalp);
err:
      usal_sfree(usalp);
      return ((SCSI *)0);
}

void
usal_sfree(SCSI *usalp)
{
      if (usalp->cmdstart)
            free(usalp->cmdstart);
      if (usalp->cmdstop)
            free(usalp->cmdstop);
      if (usalp->scmd)
            free(usalp->scmd);
      if (usalp->inq)
            free(usalp->inq);
      if (usalp->cap)
            free(usalp->cap);
      if (usalp->local)
            free(usalp->local);
      usal_freebuf(usalp);
      if (usalp->errstr)
            free(usalp->errstr);
      free(usalp);
}

Generated by  Doxygen 1.6.0   Back to index