📄 scsi-bsd.c
字号:
* 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 <sys/param.h>#include <cam/cam.h>#include <cam/cam_ccb.h>#include <cam/scsi/scsi_message.h>#include <cam/scsi/scsi_pass.h>#include <camlib.h>/* * 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.31"; /* The version for this transport*/#define CAM_MAXDEVS 128struct 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. */EXPORT char *scg__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);}/* * Build a list of everything we can find. */EXPORT intscsi_open(scgp, device, busno, tgt, tlun) SCSI *scgp; char *device; int busno; int tgt; int tlun;{ char name[16]; int unit; int nopen = 0; union ccb ccb; int bufsize; struct periph_match_pattern *match_pat; int fd; 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; /* * 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; sprintf(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; sprintf(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);}EXPORT intscsi_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);}LOCALlong scsi_maxdma(scgp, amt) SCSI *scgp; long amt;{#ifdef DFLTPHYS return (DFLTPHYS);#else return (MAXPHYS);#endif}EXPORT void *scsi_getbuf(scgp, amt) SCSI *scgp; long amt;{ if (amt <= 0 || amt > scsi_bufsize(scgp, amt)) return ((void *)0); if (scgp->debug) printf("scsi_getbuf: %ld bytes\n", amt); scgp->bufbase = valloc((size_t)(amt)); return (scgp->bufbase);}EXPORT voidscsi_freebuf(scgp) SCSI *scgp;{ if (scgp->bufbase) free(scgp->bufbase); scgp->bufbase = NULL;}EXPORTBOOL scsi_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);}EXPORTint scsi_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);}EXPORT intscsi_initiator_id(scgp) SCSI *scgp;{ return (-1);}EXPORTint scsi_isatapi(scgp) SCSI *scgp;{ return (FALSE);}EXPORTint scsireset(scgp) SCSI *scgp;{ /* XXX synchronous reset command - is this wise? */ return (-1);}LOCAL intscsi_send(scgp, unit, sp) SCSI *scgp; int unit; struct scg_cmd *sp;{ struct cam_device *dev; union ccb ccb_space; union ccb *ccb = &ccb_space; int rv, result; u_int32_t ccb_flags; if (unit < 0) {#if 0 fprintf(stderr, "attempt to reference invalid unit %d\n", unit);#endif sp->error = SCG_FATAL; return (0); } dev = scglocal(scgp)->cam_devices[unit]; 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) { ccb_flags = (sp->flags & SCG_RECV_DATA) ? CAM_DIR_IN : CAM_DIR_OUT; } else { ccb_flags = CAM_DIR_NONE; } ccb_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 scsi_send(): * * ccb_flags |= CAM_DEV_QFRZDIS | CAM_TAG_ACTION_VALID; */ cam_fill_csio(&ccb->csio, /* retries */ 1, /* cbfncp */ NULL, /* flags */ ccb_flags, /* tag_action */ MSG_SIMPLE_Q_TAG, /* data_ptr */ (u_int8_t *)sp->addr, /* dxfer_len */ sp->size, /* sense_len */ SSD_FULL_SIZE, /* cdb_len */ sp->cdb_len, /* timeout */ sp->timeout * 1000); /* Run the command */ errno = 0; if ((rv = cam_send_ccb(dev, ccb)) == -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'); fillbytes(&sp->u_sense.cmd_sense, sizeof(sp->u_sense.cmd_sense), '\0'); sp->resid = ccb->csio.resid; sp->sense_count = SSD_FULL_SIZE - ccb->csio.sense_resid; /* * Determine how much room we have for sense data. */ 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -