/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }