summaryrefslogtreecommitdiff
path: root/libusal/scsiopen.c
diff options
context:
space:
mode:
Diffstat (limited to 'libusal/scsiopen.c')
-rw-r--r--libusal/scsiopen.c464
1 files changed, 464 insertions, 0 deletions
diff --git a/libusal/scsiopen.c b/libusal/scsiopen.c
new file mode 100644
index 0000000..41dd1e9
--- /dev/null
+++ b/libusal/scsiopen.c
@@ -0,0 +1,464 @@
+/*
+ * 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);
+}