/* @(#)scsi-bsd.c 1.53 15/10/08 Copyright 1997-2015 J. Schilling */ #ifndef lint static char __sccsid[] = "@(#)scsi-bsd.c 1.53 15/10/08 Copyright 1997-2015 J. Schilling"; #endif /* * Interface for the NetBSD/FreeBSD/OpenBSD generic SCSI implementation. * * This is a hack, that tries to emulate the functionality * of the scg driver. * The SCSI tranport of the generic *BSD implementation is very * similar to the SCSI command transport of the * 6 years older scg driver. * * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_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) 1997-2015 J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * The following exceptions apply: * CDDL ยง3.6 needs to be replaced by: "You may create a Larger Work by * combining Covered Software with other code if all other code is governed by * the terms of a license that is OSI approved (see www.opensource.org) and * you may distribute the Larger Work as a single product. In such a case, * You must make sure the requirements of this License are fulfilled for * the Covered Software." * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #ifndef HAVE_CAMLIB_H #undef sense #include #if defined(__NetBSD__) #ifdef USE_GETRAWPARTITION #include #endif #endif /* * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_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. */ LOCAL char _scg_trans_version[] = "scsi-bsd.c-1.53"; /* The version for this transport*/ #define MAX_SCG 32 /* Max # of SCSI controllers */ #define MAX_TGT 16 #define MAX_LUN 8 struct scg_local { short scgfiles[MAX_SCG][MAX_TGT][MAX_LUN]; }; #define scglocal(p) ((struct scg_local *)((p)->local)) /*#define MAX_DMA_BSD (32*1024)*/ #define MAX_DMA_BSD (60*1024) /* More seems to make problems */ #if defined(__NetBSD__) && defined(TYPE_ATAPI) /* * NetBSD 1.3 has a merged SCSI/ATAPI system, so this structure * is slightly different. */ #define MAYBE_ATAPI #define SADDR_ISSCSI(a) ((a).type == TYPE_SCSI) #define SADDR_BUS(a) (SADDR_ISSCSI(a)?(a).addr.scsi.scbus:(MAX_SCG-1)) #define SADDR_TARGET(a) (SADDR_ISSCSI(a)?(a).addr.scsi.target:(a).addr.atapi.atbus*2+(a).addr.atapi.drive) #define SADDR_LUN(a) (SADDR_ISSCSI(a)?(a).addr.scsi.lun:0) #else #if defined(__OpenBSD__) && defined(TYPE_ATAPI) #define SADDR_ISSCSI(a) ((a).type == TYPE_SCSI) #else #define SADDR_ISSCSI(a) (1) #endif #define SADDR_BUS(a) (a).scbus #define SADDR_TARGET(a) (a).target #define SADDR_LUN(a) (a).lun #endif /* __NetBSD__ && TYPE_ATAPI */ #if defined(__NetBSD__) || defined(__OpenBSD__) LOCAL int getslice __PR((int n)); #endif LOCAL BOOL scg_setup __PR((SCSI *scgp, int f, int busno, int tgt, int tlun)); /* * Return version information for the low level SCSI transport code. * This has been introduced to make it easier to trace down problems * in applications. */ LOCAL char * scgo_version(scgp, what) SCSI *scgp; int what; { if (scgp != (SCSI *)0) { switch (what) { case SCG_VERSION: return (_scg_trans_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth_schily); case SCG_SCCS_ID: return (__sccsid); } } return ((char *)0); } LOCAL int scgo_help(scgp, f) SCSI *scgp; FILE *f; { __scg_help(f, "SCIOCCOMMAND", "SCSI for devices known by *BSD", #if defined(__OpenBSD__) "", "device or bus,target,lun", "/dev/rcd0c:@ or 1,2,0", FALSE, TRUE); #else "", "device or bus,target,lun", "/dev/rcd0a:@ or 1,2,0", FALSE, TRUE); #endif return (0); } LOCAL int scgo_open(scgp, device) SCSI *scgp; char *device; { int busno = scg_scsibus(scgp); int tgt = scg_target(scgp); int tlun = scg_lun(scgp); register int f; register int b; register int t; register int l; register int nopen = 0; char dev_name[64]; #if defined(__NetBSD__) || defined(__OpenBSD__) #ifdef USE_GETRAWPARTITION int myslicename = getrawpartition(); #else int myslicename = 0; #endif #endif if (busno >= MAX_SCG || tgt >= MAX_TGT || tlun >= MAX_LUN) { errno = EINVAL; if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Illegal value for busno, target or lun '%d,%d,%d'", busno, tgt, tlun); return (-1); } if (scgp->local == NULL) { scgp->local = malloc(sizeof (struct scg_local)); if (scgp->local == NULL) return (0); for (b = 0; b < MAX_SCG; b++) { for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) scglocal(scgp)->scgfiles[b][t][l] = (short)-1; } } } if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) goto openbydev; #if defined(__NetBSD__) || defined(__OpenBSD__) /* * I know of no method in NetBSD/OpenBSD to probe the scsibus and get * the mapping busnumber,target,lun --> devicename. * * For now, implement a true brute force hack to find cdroms to allow * cdrecord to work, but libscg is a generic SCSI transport lib. * * Note that this method only finds cd0-cd9. For a more correct * implementation we would at least need to also scan the disk and tape * devices. * * This still does not include scanners and other SCSI devices. * Help is needed! */ if (busno >= 0 && tgt >= 0 && tlun >= 0) { struct scsi_addr mysaddr; for (l = 0; l < 10; l++) { nextslice1: sprintf(dev_name, "/dev/rcd%d%c", l, 'a' + myslicename); f = open(dev_name, O_RDWR); if (f >= 0) { if (ioctl(f, SCIOCIDENTIFY, &mysaddr) < 0) { #ifndef USE_GETRAWPARTITION if (errno == ENOTTY && myslicename < 16) { close(f); myslicename = getslice(l); goto nextslice1; } #endif close(f); errno = EINVAL; return (0); } if (busno == SADDR_BUS(mysaddr) && tgt == SADDR_TARGET(mysaddr) && tlun == SADDR_LUN(mysaddr)) { scglocal(scgp)->scgfiles[busno][tgt][tlun] = f; return (1); } } else { #if defined(__OpenBSD__) && defined(ENOMEDIUM) && defined(ENXIO) if ((errno == ENOMEDIUM || errno == ENXIO) && myslicename < 16) { myslicename = getslice(l); goto nextslice1; } #endif } } } else for (l = 0; l < 10; l++) { struct scsi_addr mysaddr; nextslice2: sprintf(dev_name, "/dev/rcd%d%c", l, 'a' + myslicename); f = open(dev_name, O_RDWR); if (f >= 0) { if (ioctl(f, SCIOCIDENTIFY, &mysaddr) < 0) { #ifndef USE_GETRAWPARTITION if (errno == ENOTTY && myslicename < 16) { close(f); myslicename = getslice(l); goto nextslice2; } #endif close(f); errno = EINVAL; return (0); } if (scg_setup(scgp, f, busno, tgt, tlun)) nopen++; } else { #if defined(__OpenBSD__) && defined(ENOMEDIUM) && defined(ENXIO) if ((errno == ENOMEDIUM || errno == ENXIO) && myslicename < 16) { myslicename = getslice(l); goto nextslice2; } #endif } } #else /* ! __NetBSD__ || __OpenBSD__ */ if (busno >= 0 && tgt >= 0 && tlun >= 0) { js_snprintf(dev_name, sizeof (dev_name), "/dev/su%d-%d-%d", busno, tgt, tlun); f = open(dev_name, O_RDWR); if (f < 0) { goto openbydev; } scglocal(scgp)->scgfiles[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++) { js_snprintf(dev_name, sizeof (dev_name), "/dev/su%d-%d-%d", b, t, l); f = open(dev_name, O_RDWR); /* error("open (%s) = %d\n", dev_name, f);*/ if (f < 0) { if (errno != ENOENT && errno != ENXIO && errno != ENODEV) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", dev_name); return (0); } } else { if (scg_setup(scgp, f, b, t, l)) nopen++; } } } } #endif /* ! __NetBSD__ || __OpebBSD__ */ /* * Could not open /dev/su-* or got dev=devname:b,l,l / dev=devname:@,l * We do the apropriate tests and try our best. */ openbydev: if (nopen == 0) { struct scsi_addr saddr; if (device == NULL || device[0] == '\0') return (0); f = open(device, O_RDWR); if (f < 0) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Cannot open '%s'", device); return (0); } if (tgt == -2) { if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) { close(f); errno = EINVAL; return (0); } busno = SADDR_BUS(saddr); tgt = SADDR_TARGET(saddr); if ((tlun >= 0) && (tlun != SADDR_LUN(saddr))) { close(f); errno = EINVAL; return (0); } tlun = SADDR_LUN(saddr); scg_settarget(scgp, busno, tgt, tlun); } if (scg_setup(scgp, f, busno, tgt, tlun)) nopen++; } return (nopen); } #if defined(__NetBSD__) || defined(__OpenBSD__) /* * Avoid to call getrawpartition() because this would force us to * link against libutil. */ LOCAL int getslice(n) int n; { struct scsi_addr saddr; char dev_name[64]; int slice; int f; for (slice = 0; slice < 16; slice++) { sprintf(dev_name, "/dev/rcd%d%c", n, 'a' + slice); f = open(dev_name, O_RDWR); if (f >= 0) { if (ioctl(f, SCIOCIDENTIFY, &saddr) >= 0) { close(f); break; } close(f); } } return (slice); } #endif LOCAL int scgo_close(scgp) SCSI *scgp; { register int f; register int b; register int t; register int l; if (scgp->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 = scglocal(scgp)->scgfiles[b][t][l]; if (f >= 0) close(f); scglocal(scgp)->scgfiles[b][t][l] = (short)-1; } } } return (0); } LOCAL BOOL scg_setup(scgp, f, busno, tgt, tlun) SCSI *scgp; int f; int busno; int tgt; int tlun; { struct scsi_addr saddr; int Bus; int Target; int Lun; BOOL onetarget = FALSE; if (scg_scsibus(scgp) >= 0 && scg_target(scgp) >= 0 && scg_lun(scgp) >= 0) onetarget = TRUE; if (ioctl(f, SCIOCIDENTIFY, &saddr) < 0) { errmsg("Cannot get SCSI addr.\n"); close(f); return (FALSE); } Bus = SADDR_BUS(saddr); Target = SADDR_TARGET(saddr); Lun = SADDR_LUN(saddr); if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "Bus: %d Target: %d Lun: %d\n", Bus, Target, Lun); } if (Bus >= MAX_SCG || Target >= MAX_TGT || Lun >= MAX_LUN) { close(f); return (FALSE); } if (scglocal(scgp)->scgfiles[Bus][Target][Lun] == (short)-1) { scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)f; return (TRUE); } if (onetarget) { if (Bus == busno && Target == tgt && Lun == tlun) { return (TRUE); } else { scglocal(scgp)->scgfiles[Bus][Target][Lun] = (short)-1; close(f); } } return (FALSE); } LOCAL long scgo_maxdma(scgp, amt) SCSI *scgp; long amt; { long maxdma = MAX_DMA_BSD; return (maxdma); } LOCAL void * scgo_getbuf(scgp, amt) SCSI *scgp; long amt; { if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt); } scgp->bufbase = valloc((size_t)(amt)); return (scgp->bufbase); } LOCAL void scgo_freebuf(scgp) SCSI *scgp; { if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL; } LOCAL int scgo_numbus(scgp) SCSI *scgp; { return (MAX_SCG); } LOCAL BOOL scgo_havebus(scgp, busno) SCSI *scgp; int busno; { register int t; register int l; if (busno < 0 || busno >= MAX_SCG) return (FALSE); if (scgp->local == NULL) return (FALSE); for (t = 0; t < MAX_TGT; t++) { for (l = 0; l < MAX_LUN; l++) if (scglocal(scgp)->scgfiles[busno][t][l] >= 0) return (TRUE); } return (FALSE); } LOCAL int scgo_fileno(scgp, busno, tgt, tlun) SCSI *scgp; int busno; int tgt; int tlun; { if (busno < 0 || busno >= MAX_SCG || tgt < 0 || tgt >= MAX_TGT || tlun < 0 || tlun >= MAX_LUN) return (-1); if (scgp->local == NULL) return (-1); return ((int)scglocal(scgp)->scgfiles[busno][tgt][tlun]); } LOCAL int scgo_initiator_id(scgp) SCSI *scgp; { return (-1); } LOCAL int scgo_isatapi(scgp) SCSI *scgp; { #ifdef MAYBE_ATAPI struct scsi_addr saddr; if (ioctl(scgp->fd, SCIOCIDENTIFY, &saddr) < 0) return (-1); if (!SADDR_ISSCSI(saddr)) return (TRUE); #endif return (FALSE); } LOCAL int scgo_reset(scgp, what) SCSI *scgp; int what; { if (what == SCG_RESET_NOP) return (0); if (what != SCG_RESET_BUS) { errno = EINVAL; return (-1); } /* * XXX Does this reset TGT or BUS ??? */ return (ioctl(scgp->fd, SCIOCRESET, 0)); } LOCAL int scgo_send(scgp) SCSI *scgp; { struct scg_cmd *sp = scgp->scmd; scsireq_t req; register long *lp1; register long *lp2; int ret = 0; /* js_fprintf((FILE *)scgp->errfile, "fd: %d\n", scgp->fd);*/ if (scgp->fd < 0) { sp->error = SCG_FATAL; return (0); } req.flags = SCCMD_ESCAPE; /* We set the SCSI cmd len */ if (sp->flags & SCG_RECV_DATA) req.flags |= SCCMD_READ; else if (sp->size > 0) req.flags |= SCCMD_WRITE; req.timeout = sp->timeout * 1000; lp1 = (long *)sp->cdb.cmd_cdb; lp2 = (long *)req.cmd; *lp2++ = *lp1++; *lp2++ = *lp1++; *lp2++ = *lp1++; *lp2++ = *lp1++; req.cmdlen = sp->cdb_len; req.databuf = sp->addr; req.datalen = sp->size; req.datalen_used = 0; fillbytes(req.sense, sizeof (req.sense), '\0'); if (sp->sense_len > sizeof (req.sense)) req.senselen = sizeof (req.sense); else if (sp->sense_len < 0) req.senselen = 0; else req.senselen = sp->sense_len; req.senselen_used = 0; req.status = 0; req.retsts = 0; req.error = 0; if (ioctl(scgp->fd, SCIOCCOMMAND, (void *)&req) < 0) { ret = -1; sp->ux_errno = geterrno(); if (sp->ux_errno != ENOTTY) ret = 0; } else { sp->ux_errno = 0; if (req.retsts != SCCMD_OK) sp->ux_errno = EIO; } fillbytes(&sp->scb, sizeof (sp->scb), '\0'); fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0'); sp->resid = req.datalen - req.datalen_used; sp->sense_count = req.senselen_used; if (sp->sense_count > SCG_MAX_SENSE) sp->sense_count = SCG_MAX_SENSE; movebytes(req.sense, sp->u_sense.cmd_sense, sp->sense_count); sp->u_scb.cmd_scb[0] = req.status; switch (req.retsts) { case SCCMD_OK: #ifdef BSD_SCSI_SENSE_BUG sp->u_scb.cmd_scb[0] = 0; sp->ux_errno = 0; #endif sp->error = SCG_NO_ERROR; break; case SCCMD_TIMEOUT: sp->error = SCG_TIMEOUT; break; default: case SCCMD_BUSY: sp->error = SCG_RETRYABLE; break; case SCCMD_SENSE: sp->error = SCG_RETRYABLE; break; case SCCMD_UNKNOWN: sp->error = SCG_FATAL; break; } return (ret); } #define sense u_sense.Sense #else /* BSD_CAM */ /* * Interface for the FreeBSD CAM passthrough device. * * Copyright (c) 1998 Michael Smith * Copyright (c) 1998 Kenneth D. Merry * Copyright (c) 1998-2011 Joerg Schilling * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #undef sense #define scsi_sense CAM_scsi_sense #define scsi_inquiry CAM_scsi_inquiry #include #include #include #include #include #include /* * Warning: you may change this source, but if you do that * you need to change the _scg_version and _scg_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. */ LOCAL char _scg_trans_version[] = "scsi-bsd.c-1.53"; /* The version for this transport*/ #define CAM_MAXDEVS 128 struct scg_local { struct cam_device *cam_devices[CAM_MAXDEVS + 1]; }; #define scglocal(p) ((struct scg_local *)((p)->local)) /* * Return version information for the low level SCSI transport code. * This has been introduced to make it easier to trace down problems * in applications. */ LOCAL char * scgo_version(scgp, what) SCSI *scgp; int what; { if (scgp != (SCSI *)0) { switch (what) { case SCG_VERSION: return (_scg_trans_version); /* * If you changed this source, you are not allowed to * return "schily" for the SCG_AUTHOR request. */ case SCG_AUTHOR: return (_scg_auth_schily); case SCG_SCCS_ID: return (__sccsid); } } return ((char *)0); } LOCAL int scgo_help(scgp, f) SCSI *scgp; FILE *f; { __scg_help(f, "CAM", "Generic transport independent SCSI (Common Access Method)", "", "bus,target,lun", "1,2,0", TRUE, FALSE); return (0); } /* * Build a list of everything we can find. */ LOCAL int scgo_open(scgp, device) SCSI *scgp; char *device; { int busno = scg_scsibus(scgp); int tgt = scg_target(scgp); int tlun = scg_lun(scgp); char name[16]; int unit; int nopen = 0; union ccb ccb; int bufsize; struct periph_match_pattern *match_pat; int fd; seterrno(0); if ((device != NULL && *device != '\0') || (busno == -2 && tgt == -2)) { errno = EINVAL; if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Open by 'devname' not supported on this OS"); return (-1); } if (scgp->local == NULL) { scgp->local = malloc(sizeof (struct scg_local)); if (scgp->local == NULL) return (0); for (unit = 0; unit <= CAM_MAXDEVS; unit++) { scglocal(scgp)->cam_devices[unit] = (struct cam_device *)-1; } } /* * If we're not scanning the bus, just open one device. */ if (busno >= 0 && tgt >= 0 && tlun >= 0) { scglocal(scgp)->cam_devices[0] = cam_open_btl(busno, tgt, tlun, O_RDWR, NULL); if (scglocal(scgp)->cam_devices[0] == NULL) return (-1); nopen++; return (nopen); } /* * First open the transport layer device. There's no point in the * rest of this if we can't open it. */ if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Open of %s failed", XPT_DEVICE); return (-1); } fillbytes(&ccb, sizeof (union ccb), '\0'); /* * Get a list of up to CAM_MAXDEVS passthrough devices in the * system. */ ccb.ccb_h.func_code = XPT_DEV_MATCH; ccb.ccb_h.path_id = CAM_XPT_PATH_ID; ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; /* * Setup the result buffer. */ bufsize = sizeof (struct dev_match_result) * CAM_MAXDEVS; ccb.cdm.match_buf_len = bufsize; ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); if (ccb.cdm.matches == NULL) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Couldn't malloc match buffer"); close(fd); return (-1); } ccb.cdm.num_matches = 0; /* * Setup the pattern buffer. We're matching against all * peripherals named "pass". */ ccb.cdm.num_patterns = 1; ccb.cdm.pattern_buf_len = sizeof (struct dev_match_pattern); ccb.cdm.patterns = (struct dev_match_pattern *)malloc( sizeof (struct dev_match_pattern)); if (ccb.cdm.patterns == NULL) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Couldn't malloc pattern buffer"); close(fd); free(ccb.cdm.matches); return (-1); } ccb.cdm.patterns[0].type = DEV_MATCH_PERIPH; match_pat = &ccb.cdm.patterns[0].pattern.periph_pattern; js_snprintf(match_pat->periph_name, sizeof (match_pat->periph_name), "pass"); match_pat->flags = PERIPH_MATCH_NAME; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "CAMIOCOMMAND ioctl failed"); close(fd); free(ccb.cdm.matches); free(ccb.cdm.patterns); return (-1); } if ((ccb.ccb_h.status != CAM_REQ_CMP) || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { /* errmsgno(EX_BAD, "Got CAM error 0x%X, CDM error %d.\n",*/ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Got CAM error 0x%X, CDM error %d", ccb.ccb_h.status, ccb.cdm.status); close(fd); free(ccb.cdm.matches); free(ccb.cdm.patterns); return (-1); } free(ccb.cdm.patterns); close(fd); for (unit = 0; unit < MIN(CAM_MAXDEVS, ccb.cdm.num_matches); unit++) { struct periph_match_result *periph_result; /* * We shouldn't have anything other than peripheral * matches in here. If we do, it means an error in the * device matching code in the transport layer. */ if (ccb.cdm.matches[unit].type != DEV_MATCH_PERIPH) { /* errmsgno(EX_BAD, "Kernel error! got periph match type %d!!\n",*/ if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Kernel error! got periph match type %d!!", ccb.cdm.matches[unit].type); free(ccb.cdm.matches); return (-1); } periph_result = &ccb.cdm.matches[unit].result.periph_result; js_snprintf(name, sizeof (name), "/dev/%s%d", periph_result->periph_name, periph_result->unit_number); /* * cam_open_pass() avoids all lookup and translation from * "regular device name" to passthrough unit number and * just opens the device in question as a passthrough device. */ scglocal(scgp)->cam_devices[unit] = cam_open_pass(name, O_RDWR, NULL); /* * If we get back NULL from the open routine, it wasn't * able to open the given passthrough device for one reason * or another. */ if (scglocal(scgp)->cam_devices[unit] == NULL) { #ifdef OLD errmsgno(EX_BAD, "Error opening /dev/%s%d\n", periph_result->periph_name, periph_result->unit_number); errmsgno(EX_BAD, "%s\n", cam_errbuf); #endif if (scgp->errstr) js_snprintf(scgp->errstr, SCSI_ERRSTR_SIZE, "Error opening /dev/%s%d Cam error '%s'", periph_result->periph_name, periph_result->unit_number, cam_errbuf); break; } nopen++; } free(ccb.cdm.matches); return (nopen); } LOCAL int scgo_close(scgp) SCSI *scgp; { register int i; if (scgp->local == NULL) return (-1); for (i = 0; i <= CAM_MAXDEVS; i++) { if (scglocal(scgp)->cam_devices[i] != (struct cam_device *)-1) cam_close_device(scglocal(scgp)->cam_devices[i]); scglocal(scgp)->cam_devices[i] = (struct cam_device *)-1; } return (0); } LOCAL long scgo_maxdma(scgp, amt) SCSI *scgp; long amt; { #ifdef DFLTPHYS return (DFLTPHYS); #else return (MAXPHYS); #endif } LOCAL void * scgo_getbuf(scgp, amt) SCSI *scgp; long amt; { if (scgp->debug > 0) { js_fprintf((FILE *)scgp->errfile, "scgo_getbuf: %ld bytes\n", amt); } scgp->bufbase = valloc((size_t)(amt)); return (scgp->bufbase); } LOCAL void scgo_freebuf(scgp) SCSI *scgp; { if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL; } LOCAL int scgo_numbus(scgp) SCSI *scgp; { unsigned int unit; unsigned int maxbus = 0; BOOL found_bus = FALSE; if (scgp->local == NULL) return (0); /* * There's a "cleaner" way to do this, using the matching code, but * it would involve more code than this solution... */ for (unit = 0; scglocal(scgp)->cam_devices[unit] != (struct cam_device *)-1; unit++) { if (scglocal(scgp)->cam_devices[unit] == NULL) continue; found_bus = TRUE; /* * path_id is unsigned, so maxbus cannot be initialized to -1. */ if (scglocal(scgp)->cam_devices[unit]->path_id > maxbus) maxbus = scglocal(scgp)->cam_devices[unit]->path_id; } if (!found_bus) return (0); return (maxbus+1); } LOCAL BOOL scgo_havebus(scgp, busno) SCSI *scgp; int busno; { int unit; if (scgp->local == NULL) return (FALSE); /* * There's a "cleaner" way to do this, using the matching code, but * it would involve more code than this solution... */ for (unit = 0; scglocal(scgp)->cam_devices[unit] != (struct cam_device *)-1; unit++) { if (scglocal(scgp)->cam_devices[unit] == NULL) continue; if (scglocal(scgp)->cam_devices[unit]->path_id == busno) return (TRUE); } return (FALSE); } LOCAL int scgo_fileno(scgp, busno, unit, tlun) SCSI *scgp; int busno; int unit; int tlun; { int i; if (scgp->local == NULL) return (-1); for (i = 0; scglocal(scgp)->cam_devices[i] != (struct cam_device *)-1; i++) { if (scglocal(scgp)->cam_devices[i] == NULL) continue; if ((scglocal(scgp)->cam_devices[i]->path_id == busno) && (scglocal(scgp)->cam_devices[i]->target_id == unit) && (scglocal(scgp)->cam_devices[i]->target_lun == tlun)) return (i); } return (-1); } LOCAL int scgo_initiator_id(scgp) SCSI *scgp; { return (-1); } LOCAL int scgo_isatapi(scgp) SCSI *scgp; { return (FALSE); } LOCAL int scgo_reset(scgp, what) SCSI *scgp; int what; { /* XXX synchronous reset command - is this wise? */ errno = EINVAL; return (-1); } LOCAL int scgo_send(scgp) SCSI *scgp; { struct scg_cmd *sp = scgp->scmd; struct cam_device *dev; union ccb ccb_space; union ccb *ccb = &ccb_space; int rv, result; u_int32_t flags; int sense_len; if (scgp->fd < 0) { #if 0 js_fprintf((FILE *)scgp->errfile, "attempt to reference invalid unit %d\n", scgp->fd); #endif sp->error = SCG_FATAL; return (0); } dev = scglocal(scgp)->cam_devices[scgp->fd]; fillbytes(&ccb->ccb_h, sizeof (struct ccb_hdr), '\0'); ccb->ccb_h.path_id = dev->path_id; ccb->ccb_h.target_id = dev->target_id; ccb->ccb_h.target_lun = dev->target_lun; /* Build the CCB */ fillbytes(&(&ccb->ccb_h)[1], sizeof (struct ccb_scsiio), '\0'); movebytes(sp->cdb.cmd_cdb, &ccb->csio.cdb_io.cdb_bytes, sp->cdb_len); /* * Set the data direction flags. */ if (sp->size != 0) { flags = (sp->flags & SCG_RECV_DATA) ? CAM_DIR_IN : CAM_DIR_OUT; } else { flags = CAM_DIR_NONE; } flags |= CAM_DEV_QFRZDIS; /* * If you don't want to bother with sending tagged commands under CAM, * we don't need to do anything to cdrecord. If you want to send * tagged commands to those devices that support it, we'll need to set * the tag action valid field like this in scgo_send(): * * flags |= CAM_DEV_QFRZDIS | CAM_TAG_ACTION_VALID; * * Note that SSD_FULL_SIZE is 32 on FreeBSD and that it is impossible * to get more that SSD_FULL_SIZE == sizeof ((struct scsi_sense_data) * on FreeBSD. */ sense_len = sp->sense_len; if (sense_len > SSD_FULL_SIZE) sense_len = SSD_FULL_SIZE; cam_fill_csio(&ccb->csio, /* retries */ 1, /* cbfncp */ NULL, /* flags */ flags, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ (u_int8_t *)sp->addr, /* dxfer_len */ sp->size, /* sense_len */ sense_len, /* cdb_len */ sp->cdb_len, /* timeout */ sp->timeout * 1000); /* Run the command */ errno = 0; result = 16; do { /* * CAM_REQUEUE_REQ is a very unspecified status code. For this * reason, only the kernel could know why it happened. * This is therefore a loop that should be handled inside the * kernel in order not to break the layering model. * FreBSD unfortunately has dozens of places in the kernel that * set CAM_REQUEUE_REQ and most of the places that set * CAM_REQUEUE_REQ are handling missing kernel resources. As * it seems to be unlikely that there will be a clean fix in * the kernel soon, we are forced to handle this status here. * * Note that for May 2010, there is at least one definite bug * in the kernel where CAM_REQUEUE_REQ is returned for a SCSI * reservation conflict. */ rv = cam_send_ccb(dev, ccb); } while (rv != -1 && --result > 0 && (ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQUEUE_REQ); if (rv == -1) { return (rv); } else { /* * Check for command status. Selection timeouts are fatal. * For command timeouts, we pass back the appropriate * error. If we completed successfully, there's (obviously) * no error. We declare anything else "retryable". */ switch (ccb->ccb_h.status & CAM_STATUS_MASK) { case CAM_SEL_TIMEOUT: result = SCG_FATAL; break; case CAM_CMD_TIMEOUT: result = SCG_TIMEOUT; break; case CAM_REQ_CMP: result = SCG_NO_ERROR; break; default: result = SCG_RETRYABLE; break; } } sp->error = result; if (result != SCG_NO_ERROR) sp->ux_errno = EIO; /* Pass the result back up */ fillbytes(&sp->scb, sizeof (sp->scb), '\0'); #ifdef CLEAR_SENSE fillbytes(&sp->u_sense.cmd_sense, sizeof (sp->u_sense.cmd_sense), '\0'); #endif sp->resid = ccb->csio.resid; sp->sense_count = sense_len - ccb->csio.sense_resid; /* * Determine how much room we have for sense data in struct scg_cmd. */ if (sp->sense_count > SCG_MAX_SENSE) sp->sense_count = SCG_MAX_SENSE; /* Copy the sense data out */ movebytes(&ccb->csio.sense_data, &sp->u_sense.cmd_sense, sp->sense_count); sp->u_scb.cmd_scb[0] = ccb->csio.scsi_status; return (0); } #undef scsi_sense #undef scsi_inquiry #define sense u_sense.Sense #endif /* BSD_CAM */