diff options
Diffstat (limited to 'libusal/scsiopen.c')
-rw-r--r-- | libusal/scsiopen.c | 464 |
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); +} |