diff --git a/sbin/camcontrol/camcontrol.c b/sbin/camcontrol/camcontrol.c index 1d204f148b39..f415c2b6ab14 100644 --- a/sbin/camcontrol/camcontrol.c +++ b/sbin/camcontrol/camcontrol.c @@ -1,2988 +1,3021 @@ /* - * Copyright (c) 1997, 1998, 1999 Kenneth D. Merry + * Copyright (c) 1997, 1998, 1999, 2000 Kenneth D. Merry * 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. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "camcontrol.h" #define DEFAULT_DEVICE "da" #define DEFAULT_UNIT 0 typedef enum { CAM_ARG_NONE = 0x00000000, CAM_ARG_DEVLIST = 0x00000001, CAM_ARG_TUR = 0x00000002, CAM_ARG_INQUIRY = 0x00000003, CAM_ARG_STARTSTOP = 0x00000004, CAM_ARG_RESCAN = 0x00000005, CAM_ARG_READ_DEFECTS = 0x00000006, CAM_ARG_MODE_PAGE = 0x00000007, CAM_ARG_SCSI_CMD = 0x00000008, CAM_ARG_DEVTREE = 0x00000009, CAM_ARG_USAGE = 0x0000000a, CAM_ARG_DEBUG = 0x0000000b, CAM_ARG_RESET = 0x0000000c, CAM_ARG_FORMAT = 0x0000000d, CAM_ARG_TAG = 0x0000000e, CAM_ARG_RATE = 0x0000000f, CAM_ARG_OPT_MASK = 0x0000000f, CAM_ARG_VERBOSE = 0x00000010, CAM_ARG_DEVICE = 0x00000020, CAM_ARG_BUS = 0x00000040, CAM_ARG_TARGET = 0x00000080, CAM_ARG_LUN = 0x00000100, CAM_ARG_EJECT = 0x00000200, CAM_ARG_UNIT = 0x00000400, CAM_ARG_FORMAT_BLOCK = 0x00000800, CAM_ARG_FORMAT_BFI = 0x00001000, CAM_ARG_FORMAT_PHYS = 0x00002000, CAM_ARG_PLIST = 0x00004000, CAM_ARG_GLIST = 0x00008000, CAM_ARG_GET_SERIAL = 0x00010000, CAM_ARG_GET_STDINQ = 0x00020000, CAM_ARG_GET_XFERRATE = 0x00040000, CAM_ARG_INQ_MASK = 0x00070000, CAM_ARG_MODE_EDIT = 0x00080000, CAM_ARG_PAGE_CNTL = 0x00100000, CAM_ARG_TIMEOUT = 0x00200000, CAM_ARG_CMD_IN = 0x00400000, CAM_ARG_CMD_OUT = 0x00800000, CAM_ARG_DBD = 0x01000000, CAM_ARG_ERR_RECOVER = 0x02000000, CAM_ARG_RETRIES = 0x04000000, CAM_ARG_START_UNIT = 0x08000000, CAM_ARG_DEBUG_INFO = 0x10000000, CAM_ARG_DEBUG_TRACE = 0x20000000, CAM_ARG_DEBUG_SUBTRACE = 0x40000000, CAM_ARG_DEBUG_CDB = 0x80000000, CAM_ARG_FLAG_MASK = 0xfffffff0 } cam_argmask; struct camcontrol_opts { char *optname; cam_argmask argnum; const char *subopt; }; extern int optreset; static const char scsicmd_opts[] = "c:i:o:"; static const char readdefect_opts[] = "f:GP"; static const char negotiate_opts[] = "acD:O:qR:T:UW:"; struct camcontrol_opts option_table[] = { {"tur", CAM_ARG_TUR, NULL}, {"inquiry", CAM_ARG_INQUIRY, "DSR"}, {"start", CAM_ARG_STARTSTOP | CAM_ARG_START_UNIT, NULL}, {"stop", CAM_ARG_STARTSTOP, NULL}, {"eject", CAM_ARG_STARTSTOP | CAM_ARG_EJECT, NULL}, {"rescan", CAM_ARG_RESCAN, NULL}, {"reset", CAM_ARG_RESET, NULL}, {"cmd", CAM_ARG_SCSI_CMD, scsicmd_opts}, {"command", CAM_ARG_SCSI_CMD, scsicmd_opts}, {"defects", CAM_ARG_READ_DEFECTS, readdefect_opts}, {"defectlist", CAM_ARG_READ_DEFECTS, readdefect_opts}, {"devlist", CAM_ARG_DEVTREE, NULL}, {"periphlist", CAM_ARG_DEVLIST, NULL}, {"modepage", CAM_ARG_MODE_PAGE, "dem:P:"}, {"tags", CAM_ARG_TAG, "N:q"}, {"negotiate", CAM_ARG_RATE, negotiate_opts}, {"rate", CAM_ARG_RATE, negotiate_opts}, {"debug", CAM_ARG_DEBUG, "ITSc"}, {"help", CAM_ARG_USAGE, NULL}, {"-?", CAM_ARG_USAGE, NULL}, {"-h", CAM_ARG_USAGE, NULL}, {NULL, 0, NULL} }; typedef enum { CC_OR_NOT_FOUND, CC_OR_AMBIGUOUS, CC_OR_FOUND } camcontrol_optret; cam_argmask arglist; int bus, target, lun; camcontrol_optret getoption(char *arg, cam_argmask *argnum, char **subopt); static int getdevlist(struct cam_device *device); static int getdevtree(void); static int testunitready(struct cam_device *device, int retry_count, int timeout, int quiet); static int scsistart(struct cam_device *device, int startstop, int loadeject, int retry_count, int timeout); static int scsidoinquiry(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int scsiinquiry(struct cam_device *device, int retry_count, int timeout); static int scsiserial(struct cam_device *device, int retry_count, int timeout); static int scsixferrate(struct cam_device *device); static int parse_btl(char *tstr, int *bus, int *target, int *lun, cam_argmask *arglist); static int dorescan_or_reset(int argc, char **argv, int rescan); static int rescan_or_reset_bus(int bus, int rescan); static int scanlun_or_reset_dev(int bus, int target, int lun, int scan); static int readdefects(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static void modepage(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); static int tagcontrol(struct cam_device *device, int argc, char **argv, char *combinedopt); static void cts_print(struct cam_device *device, struct ccb_trans_settings *cts); static void cpi_print(struct ccb_pathinq *cpi); static int get_cpi(struct cam_device *device, struct ccb_pathinq *cpi); static int get_print_cts(struct cam_device *device, int user_settings, int quiet, struct ccb_trans_settings *cts); static int ratecontrol(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt); camcontrol_optret getoption(char *arg, cam_argmask *argnum, char **subopt) { struct camcontrol_opts *opts; int num_matches = 0; for (opts = option_table; (opts != NULL) && (opts->optname != NULL); opts++) { if (strncmp(opts->optname, arg, strlen(arg)) == 0) { *argnum = opts->argnum; *subopt = (char *)opts->subopt; if (++num_matches > 1) return(CC_OR_AMBIGUOUS); } } if (num_matches > 0) return(CC_OR_FOUND); else return(CC_OR_NOT_FOUND); } static int getdevlist(struct cam_device *device) { union ccb *ccb; char status[32]; int error = 0; ccb = cam_getccb(device); ccb->ccb_h.func_code = XPT_GDEVLIST; ccb->ccb_h.flags = CAM_DIR_NONE; ccb->ccb_h.retry_count = 1; ccb->cgdl.index = 0; ccb->cgdl.status = CAM_GDEVLIST_MORE_DEVS; while (ccb->cgdl.status == CAM_GDEVLIST_MORE_DEVS) { if (cam_send_ccb(device, ccb) < 0) { perror("error getting device list"); cam_freeccb(ccb); return(1); } status[0] = '\0'; switch (ccb->cgdl.status) { case CAM_GDEVLIST_MORE_DEVS: strcpy(status, "MORE"); break; case CAM_GDEVLIST_LAST_DEVICE: strcpy(status, "LAST"); break; case CAM_GDEVLIST_LIST_CHANGED: strcpy(status, "CHANGED"); break; case CAM_GDEVLIST_ERROR: strcpy(status, "ERROR"); error = 1; break; } fprintf(stdout, "%s%d: generation: %d index: %d status: %s\n", ccb->cgdl.periph_name, ccb->cgdl.unit_number, ccb->cgdl.generation, ccb->cgdl.index, status); /* * If the list has changed, we need to start over from the * beginning. */ if (ccb->cgdl.status == CAM_GDEVLIST_LIST_CHANGED) ccb->cgdl.index = 0; } cam_freeccb(ccb); return(error); } static int getdevtree(void) { union ccb ccb; int bufsize, i, fd; int need_close = 0; int error = 0; int skip_device = 0; if ((fd = open(XPT_DEVICE, O_RDWR)) == -1) { warn("couldn't open %s", XPT_DEVICE); return(1); } bzero(&(&ccb.ccb_h)[1], sizeof(struct ccb_dev_match) - sizeof(struct ccb_hdr)); ccb.ccb_h.func_code = XPT_DEV_MATCH; bufsize = sizeof(struct dev_match_result) * 100; ccb.cdm.match_buf_len = bufsize; ccb.cdm.matches = (struct dev_match_result *)malloc(bufsize); ccb.cdm.num_matches = 0; /* * We fetch all nodes, since we display most of them in the default * case, and all in the verbose case. */ ccb.cdm.num_patterns = 0; ccb.cdm.pattern_buf_len = 0; /* * We do the ioctl multiple times if necessary, in case there are * more than 100 nodes in the EDT. */ do { if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { warn("error sending CAMIOCOMMAND ioctl"); error = 1; break; } if ((ccb.ccb_h.status != CAM_REQ_CMP) || ((ccb.cdm.status != CAM_DEV_MATCH_LAST) && (ccb.cdm.status != CAM_DEV_MATCH_MORE))) { fprintf(stderr, "got CAM error %#x, CDM error %d\n", ccb.ccb_h.status, ccb.cdm.status); error = 1; break; } for (i = 0; i < ccb.cdm.num_matches; i++) { switch(ccb.cdm.matches[i].type) { case DEV_MATCH_BUS: { struct bus_match_result *bus_result; /* * Only print the bus information if the * user turns on the verbose flag. */ if ((arglist & CAM_ARG_VERBOSE) == 0) break; bus_result = &ccb.cdm.matches[i].result.bus_result; if (need_close) { fprintf(stdout, ")\n"); need_close = 0; } fprintf(stdout, "scbus%d on %s%d bus %d:\n", bus_result->path_id, bus_result->dev_name, bus_result->unit_number, bus_result->bus_id); break; } case DEV_MATCH_DEVICE: { struct device_match_result *dev_result; char vendor[16], product[48], revision[16]; char tmpstr[256]; dev_result = &ccb.cdm.matches[i].result.device_result; if ((dev_result->flags & DEV_RESULT_UNCONFIGURED) && ((arglist & CAM_ARG_VERBOSE) == 0)) { skip_device = 1; break; } else skip_device = 0; cam_strvis(vendor, dev_result->inq_data.vendor, sizeof(dev_result->inq_data.vendor), sizeof(vendor)); cam_strvis(product, dev_result->inq_data.product, sizeof(dev_result->inq_data.product), sizeof(product)); cam_strvis(revision, dev_result->inq_data.revision, sizeof(dev_result->inq_data.revision), sizeof(revision)); sprintf(tmpstr, "<%s %s %s>", vendor, product, revision); if (need_close) { fprintf(stdout, ")\n"); need_close = 0; } fprintf(stdout, "%-33s at scbus%d " "target %d lun %d (", tmpstr, dev_result->path_id, dev_result->target_id, dev_result->target_lun); need_close = 1; break; } case DEV_MATCH_PERIPH: { struct periph_match_result *periph_result; periph_result = &ccb.cdm.matches[i].result.periph_result; if (skip_device != 0) break; if (need_close > 1) fprintf(stdout, ","); fprintf(stdout, "%s%d", periph_result->periph_name, periph_result->unit_number); need_close++; break; } default: fprintf(stdout, "unknown match type\n"); break; } } } while ((ccb.ccb_h.status == CAM_REQ_CMP) && (ccb.cdm.status == CAM_DEV_MATCH_MORE)); if (need_close) fprintf(stdout, ")\n"); close(fd); return(error); } static int testunitready(struct cam_device *device, int retry_count, int timeout, int quiet) { int error = 0; union ccb *ccb; ccb = cam_getccb(device); scsi_test_unit_ready(&ccb->csio, /* retries */ retry_count, /* cbfcnp */ NULL, /* tag_action */ MSG_SIMPLE_Q_TAG, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 5000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { if (quiet == 0) perror("error sending test unit ready"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { if (quiet == 0) fprintf(stdout, "Unit is ready\n"); } else { if (quiet == 0) fprintf(stdout, "Unit is not ready\n"); error = 1; if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } } cam_freeccb(ccb); return(error); } static int scsistart(struct cam_device *device, int startstop, int loadeject, int retry_count, int timeout) { union ccb *ccb; int error = 0; ccb = cam_getccb(device); /* * If we're stopping, send an ordered tag so the drive in question * will finish any previously queued writes before stopping. If * the device isn't capable of tagged queueing, or if tagged * queueing is turned off, the tag action is a no-op. */ scsi_start_stop(&ccb->csio, /* retries */ retry_count, /* cbfcnp */ NULL, /* tag_action */ startstop ? MSG_SIMPLE_Q_TAG : MSG_ORDERED_Q_TAG, /* start/stop */ startstop, /* load_eject */ loadeject, /* immediate */ 0, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 120000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { perror("error sending start unit"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) if (startstop) { fprintf(stdout, "Unit started successfully"); if (loadeject) fprintf(stdout,", Media loaded\n"); else fprintf(stdout,"\n"); } else { fprintf(stdout, "Unit stopped successfully"); if (loadeject) fprintf(stdout, ", Media ejected\n"); else fprintf(stdout, "\n"); } else { error = 1; if (startstop) fprintf(stdout, "Error received from start unit command\n"); else fprintf(stdout, "Error received from stop unit command\n"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } } cam_freeccb(ccb); return(error); } static int scsidoinquiry(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { int c; int error = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c) { case 'D': arglist |= CAM_ARG_GET_STDINQ; break; case 'R': arglist |= CAM_ARG_GET_XFERRATE; break; case 'S': arglist |= CAM_ARG_GET_SERIAL; break; default: break; } } /* * If the user didn't specify any inquiry options, he wants all of * them. */ if ((arglist & CAM_ARG_INQ_MASK) == 0) arglist |= CAM_ARG_INQ_MASK; if (arglist & CAM_ARG_GET_STDINQ) error = scsiinquiry(device, retry_count, timeout); if (error != 0) return(error); if (arglist & CAM_ARG_GET_SERIAL) scsiserial(device, retry_count, timeout); if (error != 0) return(error); if (arglist & CAM_ARG_GET_XFERRATE) error = scsixferrate(device); return(error); } static int scsiinquiry(struct cam_device *device, int retry_count, int timeout) { union ccb *ccb; struct scsi_inquiry_data *inq_buf; int error = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("couldn't allocate CCB"); return(1); } /* cam_getccb cleans up the header, caller has to zero the payload */ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); inq_buf = (struct scsi_inquiry_data *)malloc( sizeof(struct scsi_inquiry_data)); if (inq_buf == NULL) { cam_freeccb(ccb); warnx("can't malloc memory for inquiry\n"); return(1); } bzero(inq_buf, sizeof(*inq_buf)); + /* + * Note that although the size of the inquiry buffer is the full + * 256 bytes specified in the SCSI spec, we only tell the device + * that we have allocated SHORT_INQUIRY_LENGTH bytes. There are + * two reasons for this: + * + * - The SCSI spec says that when a length field is only 1 byte, + * a value of 0 will be interpreted as 256. Therefore + * scsi_inquiry() will convert an inq_len (which is passed in as + * a u_int32_t, but the field in the CDB is only 1 byte) of 256 + * to 0. Evidently, very few devices meet the spec in that + * regard. Some devices, like many Seagate disks, take the 0 as + * 0, and don't return any data. One Pioneer DVD-R drive + * returns more data than the command asked for. + * + * So, since there are numerous devices that just don't work + * right with the full inquiry size, we don't send the full size. + * + * - The second reason not to use the full inquiry data length is + * that we don't need it here. The only reason we issue a + * standard inquiry is to get the vendor name, device name, + * and revision so scsi_print_inquiry() can print them. + * + * If, at some point in the future, more inquiry data is needed for + * some reason, this code should use a procedure similar to the + * probe code. i.e., issue a short inquiry, and determine from + * the additional length passed back from the device how much + * inquiry data the device supports. Once the amount the device + * supports is determined, issue an inquiry for that amount and no + * more. + * + * KDM, 2/18/2000 + */ scsi_inquiry(&ccb->csio, /* retries */ retry_count, /* cbfcnp */ NULL, /* tag_action */ MSG_SIMPLE_Q_TAG, /* inq_buf */ (u_int8_t *)inq_buf, - /* inq_len */ sizeof(struct scsi_inquiry_data), + /* inq_len */ SHORT_INQUIRY_LENGTH, /* evpd */ 0, /* page_code */ 0, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 5000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { perror("error sending SCSI inquiry"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { error = 1; if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } } cam_freeccb(ccb); if (error != 0) { free(inq_buf); return(error); } fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num); scsi_print_inquiry(inq_buf); free(inq_buf); return(0); } static int scsiserial(struct cam_device *device, int retry_count, int timeout) { union ccb *ccb; struct scsi_vpd_unit_serial_number *serial_buf; char serial_num[SVPD_SERIAL_NUM_SIZE + 1]; int error = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("couldn't allocate CCB"); return(1); } /* cam_getccb cleans up the header, caller has to zero the payload */ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); serial_buf = (struct scsi_vpd_unit_serial_number *) malloc(sizeof(*serial_buf)); if (serial_buf == NULL) { cam_freeccb(ccb); warnx("can't malloc memory for serial number"); return(1); } scsi_inquiry(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /* tag_action */ MSG_SIMPLE_Q_TAG, /* inq_buf */ (u_int8_t *)serial_buf, /* inq_len */ sizeof(*serial_buf), /* evpd */ 1, /* page_code */ SVPD_UNIT_SERIAL_NUMBER, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 5000); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { warn("error getting serial number"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); free(serial_buf); return(1); } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { error = 1; if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } } cam_freeccb(ccb); if (error != 0) { free(serial_buf); return(error); } bcopy(serial_buf->serial_num, serial_num, serial_buf->length); serial_num[serial_buf->length] = '\0'; if ((arglist & CAM_ARG_GET_STDINQ) || (arglist & CAM_ARG_GET_XFERRATE)) fprintf(stdout, "%s%d: Serial Number ", device->device_name, device->dev_unit_num); fprintf(stdout, "%.60s\n", serial_num); free(serial_buf); return(0); } static int scsixferrate(struct cam_device *device) { u_int32_t freq; u_int32_t speed; union ccb *ccb; u_int mb; int retval = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("couldn't allocate CCB"); return(1); } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS; ccb->cts.flags = CCB_TRANS_CURRENT_SETTINGS; if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { char *error_string = "error getting transfer settings"; if (retval < 0) warn(error_string); else warnx(error_string); /* * If there is an error, it won't be a SCSI error since * this isn't a SCSI CCB. */ if (arglist & CAM_ARG_VERBOSE) fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); retval = 1; goto xferrate_bailout; } if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) && (ccb->cts.sync_offset != 0)) { freq = scsi_calc_syncsrate(ccb->cts.sync_period); speed = freq; } else { struct ccb_pathinq cpi; retval = get_cpi(device, &cpi); if (retval != 0) goto xferrate_bailout; speed = cpi.base_transfer_speed; freq = 0; } fprintf(stdout, "%s%d: ", device->device_name, device->dev_unit_num); if ((ccb->cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) speed *= (0x01 << device->bus_width); mb = speed / 1000; if (mb > 0) fprintf(stdout, "%d.%03dMB/s transfers ", mb, speed % 1000); else fprintf(stdout, "%dKB/s transfers ", (speed % 1000) * 1000); if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) && (ccb->cts.sync_offset != 0)) fprintf(stdout, "(%d.%03dMHz, offset %d", freq / 1000, freq % 1000, ccb->cts.sync_offset); if (((ccb->cts.valid & CCB_TRANS_BUS_WIDTH_VALID) != 0) && (ccb->cts.bus_width > 0)) { if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) && (ccb->cts.sync_offset != 0)) { fprintf(stdout, ", "); } else { fprintf(stdout, " ("); } fprintf(stdout, "%dbit)", 8 * (0x01 << ccb->cts.bus_width)); } else if (((ccb->cts.valid & CCB_TRANS_SYNC_OFFSET_VALID) != 0) && (ccb->cts.sync_offset != 0)) { fprintf(stdout, ")"); } if (device->inq_data.flags & SID_CmdQue) fprintf(stdout, ", Tagged Queueing Enabled"); fprintf(stdout, "\n"); xferrate_bailout: cam_freeccb(ccb); return(retval); } /* * Parse out a bus, or a bus, target and lun in the following * format: * bus * bus:target * bus:target:lun * * Returns the number of parsed components, or 0. */ static int parse_btl(char *tstr, int *bus, int *target, int *lun, cam_argmask *arglist) { char *tmpstr; int convs = 0; while (isspace(*tstr) && (*tstr != '\0')) tstr++; tmpstr = (char *)strtok(tstr, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *bus = strtol(tmpstr, NULL, 0); *arglist |= CAM_ARG_BUS; convs++; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *target = strtol(tmpstr, NULL, 0); *arglist |= CAM_ARG_TARGET; convs++; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')) { *lun = strtol(tmpstr, NULL, 0); *arglist |= CAM_ARG_LUN; convs++; } } } return convs; } static int dorescan_or_reset(int argc, char **argv, int rescan) { static const char *must = "you must specify a bus, or a bus:target:lun to %s"; int rv, error = 0; int bus = -1, target = -1, lun = -1; if (argc < 3) { warnx(must, rescan? "rescan" : "reset"); return(1); } rv = parse_btl(argv[optind], &bus, &target, &lun, &arglist); if (rv != 1 && rv != 3) { warnx(must, rescan? "rescan" : "reset"); return(1); } if ((arglist & CAM_ARG_BUS) && (arglist & CAM_ARG_TARGET) && (arglist & CAM_ARG_LUN)) error = scanlun_or_reset_dev(bus, target, lun, rescan); else error = rescan_or_reset_bus(bus, rescan); return(error); } static int rescan_or_reset_bus(int bus, int rescan) { union ccb ccb; int fd; if (bus < 0) { warnx("invalid bus number %d", bus); return(1); } if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) { warnx("error opening tranport layer device %s", XPT_DEVICE); warn("%s", XPT_DEVICE); return(1); } ccb.ccb_h.func_code = rescan? XPT_SCAN_BUS : XPT_RESET_BUS; ccb.ccb_h.path_id = bus; ccb.ccb_h.target_id = CAM_TARGET_WILDCARD; ccb.ccb_h.target_lun = CAM_LUN_WILDCARD; ccb.crcn.flags = CAM_FLAG_NONE; /* run this at a low priority */ ccb.ccb_h.pinfo.priority = 5; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { warn("CAMIOCOMMAND ioctl failed"); close(fd); return(1); } close(fd); if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) { fprintf(stdout, "%s of bus %d was successful\n", rescan? "Re-scan" : "Reset", bus); return(0); } else { fprintf(stdout, "%s of bus %d returned error %#x\n", rescan? "Re-scan" : "Reset", bus, ccb.ccb_h.status & CAM_STATUS_MASK); return(1); } } static int scanlun_or_reset_dev(int bus, int target, int lun, int scan) { union ccb ccb; struct cam_device *device; int fd; if (bus < 0) { warnx("invalid bus number %d", bus); return(1); } if (target < 0) { warnx("invalid target number %d", target); return(1); } if (lun < 0) { warnx("invalid lun number %d", lun); return(1); } fd = -1; bzero(&ccb, sizeof(union ccb)); if (scan) { if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) { warnx("error opening tranport layer device %s\n", XPT_DEVICE); warn("%s", XPT_DEVICE); return(1); } } else { device = cam_open_btl(bus, target, lun, O_RDWR, NULL); if (device == NULL) { warnx("%s", cam_errbuf); return(1); } } ccb.ccb_h.func_code = (scan)? XPT_SCAN_LUN : XPT_RESET_DEV; ccb.ccb_h.path_id = bus; ccb.ccb_h.target_id = target; ccb.ccb_h.target_lun = lun; ccb.ccb_h.timeout = 5000; ccb.crcn.flags = CAM_FLAG_NONE; /* run this at a low priority */ ccb.ccb_h.pinfo.priority = 5; if (scan) { if (ioctl(fd, CAMIOCOMMAND, &ccb) < 0) { warn("CAMIOCOMMAND ioctl failed"); close(fd); return(1); } } else { if (cam_send_ccb(device, &ccb) < 0) { warn("error sending XPT_RESET_DEV CCB"); cam_close_device(device); return(1); } } if (scan) close(fd); else cam_close_device(device); /* * An error code of CAM_BDR_SENT is normal for a BDR request. */ if (((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) || ((!scan) && ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_BDR_SENT))) { fprintf(stdout, "%s of %d:%d:%d was successful\n", scan? "Re-scan" : "Reset", bus, target, lun); return(0); } else { fprintf(stdout, "%s of %d:%d:%d returned error %#x\n", scan? "Re-scan" : "Reset", bus, target, lun, ccb.ccb_h.status & CAM_STATUS_MASK); return(1); } } static int readdefects(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { union ccb *ccb = NULL; struct scsi_read_defect_data_10 *rdd_cdb; u_int8_t *defect_list = NULL; u_int32_t dlist_length = 65000; u_int32_t returned_length = 0; u_int32_t num_returned = 0; u_int8_t returned_format; register int i; int c, error = 0; int lists_specified = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c){ case 'f': { char *tstr; tstr = optarg; while (isspace(*tstr) && (*tstr != '\0')) tstr++; if (strcmp(tstr, "block") == 0) arglist |= CAM_ARG_FORMAT_BLOCK; else if (strcmp(tstr, "bfi") == 0) arglist |= CAM_ARG_FORMAT_BFI; else if (strcmp(tstr, "phys") == 0) arglist |= CAM_ARG_FORMAT_PHYS; else { error = 1; warnx("invalid defect format %s", tstr); goto defect_bailout; } break; } case 'G': arglist |= CAM_ARG_GLIST; break; case 'P': arglist |= CAM_ARG_PLIST; break; default: break; } } ccb = cam_getccb(device); /* * Hopefully 65000 bytes is enough to hold the defect list. If it * isn't, the disk is probably dead already. We'd have to go with * 12 byte command (i.e. alloc_length is 32 bits instead of 16) * to hold them all. */ defect_list = malloc(dlist_length); rdd_cdb =(struct scsi_read_defect_data_10 *)&ccb->csio.cdb_io.cdb_bytes; /* * cam_getccb() zeros the CCB header only. So we need to zero the * payload portion of the ccb. */ bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); cam_fill_csio(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*flags*/ CAM_DIR_IN | ((arglist & CAM_ARG_ERR_RECOVER) ? CAM_PASS_ERR_RECOVER : 0), /*tag_action*/ MSG_SIMPLE_Q_TAG, /*data_ptr*/ defect_list, /*dxfer_len*/ dlist_length, /*sense_len*/ SSD_FULL_SIZE, /*cdb_len*/ sizeof(struct scsi_read_defect_data_10), /*timeout*/ timeout ? timeout : 5000); rdd_cdb->opcode = READ_DEFECT_DATA_10; if (arglist & CAM_ARG_FORMAT_BLOCK) rdd_cdb->format = SRDD10_BLOCK_FORMAT; else if (arglist & CAM_ARG_FORMAT_BFI) rdd_cdb->format = SRDD10_BYTES_FROM_INDEX_FORMAT; else if (arglist & CAM_ARG_FORMAT_PHYS) rdd_cdb->format = SRDD10_PHYSICAL_SECTOR_FORMAT; else { error = 1; warnx("no defect list format specified"); goto defect_bailout; } if (arglist & CAM_ARG_PLIST) { rdd_cdb->format |= SRDD10_PLIST; lists_specified++; } if (arglist & CAM_ARG_GLIST) { rdd_cdb->format |= SRDD10_GLIST; lists_specified++; } scsi_ulto2b(dlist_length, rdd_cdb->alloc_length); /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(device, ccb) < 0) { perror("error reading defect list"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } error = 1; goto defect_bailout; } if (arglist & CAM_ARG_VERBOSE) scsi_sense_print(device, &ccb->csio, stderr); returned_length = scsi_2btoul(((struct scsi_read_defect_data_hdr_10 *)defect_list)->length); returned_format = ((struct scsi_read_defect_data_hdr_10 *) defect_list)->format; if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { struct scsi_sense_data *sense; int error_code, sense_key, asc, ascq; sense = &ccb->csio.sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); /* * According to the SCSI spec, if the disk doesn't support * the requested format, it will generally return a sense * key of RECOVERED ERROR, and an additional sense code * of "DEFECT LIST NOT FOUND". So, we check for that, and * also check to make sure that the returned length is * greater than 0, and then print out whatever format the * disk gave us. */ if ((sense_key == SSD_KEY_RECOVERED_ERROR) && (asc == 0x1c) && (ascq == 0x00) && (returned_length > 0)) { warnx("requested defect format not available"); switch(returned_format & SRDDH10_DLIST_FORMAT_MASK) { case SRDD10_BLOCK_FORMAT: warnx("Device returned block format"); break; case SRDD10_BYTES_FROM_INDEX_FORMAT: warnx("Device returned bytes from index" " format"); break; case SRDD10_PHYSICAL_SECTOR_FORMAT: warnx("Device returned physical sector format"); break; default: error = 1; warnx("Device returned unknown defect" " data format %#x", returned_format); goto defect_bailout; break; /* NOTREACHED */ } } else { error = 1; warnx("Error returned from read defect data command"); goto defect_bailout; } } /* * XXX KDM I should probably clean up the printout format for the * disk defects. */ switch (returned_format & SRDDH10_DLIST_FORMAT_MASK){ case SRDDH10_PHYSICAL_SECTOR_FORMAT: { struct scsi_defect_desc_phys_sector *dlist; dlist = (struct scsi_defect_desc_phys_sector *) (defect_list + sizeof(struct scsi_read_defect_data_hdr_10)); num_returned = returned_length / sizeof(struct scsi_defect_desc_phys_sector); fprintf(stderr, "Got %d defect", num_returned); if ((lists_specified == 0) || (num_returned == 0)) { fprintf(stderr, "s.\n"); break; } else if (num_returned == 1) fprintf(stderr, ":\n"); else fprintf(stderr, "s:\n"); for (i = 0; i < num_returned; i++) { fprintf(stdout, "%d:%d:%d\n", scsi_3btoul(dlist[i].cylinder), dlist[i].head, scsi_4btoul(dlist[i].sector)); } break; } case SRDDH10_BYTES_FROM_INDEX_FORMAT: { struct scsi_defect_desc_bytes_from_index *dlist; dlist = (struct scsi_defect_desc_bytes_from_index *) (defect_list + sizeof(struct scsi_read_defect_data_hdr_10)); num_returned = returned_length / sizeof(struct scsi_defect_desc_bytes_from_index); fprintf(stderr, "Got %d defect", num_returned); if ((lists_specified == 0) || (num_returned == 0)) { fprintf(stderr, "s.\n"); break; } else if (num_returned == 1) fprintf(stderr, ":\n"); else fprintf(stderr, "s:\n"); for (i = 0; i < num_returned; i++) { fprintf(stdout, "%d:%d:%d\n", scsi_3btoul(dlist[i].cylinder), dlist[i].head, scsi_4btoul(dlist[i].bytes_from_index)); } break; } case SRDDH10_BLOCK_FORMAT: { struct scsi_defect_desc_block *dlist; dlist = (struct scsi_defect_desc_block *)(defect_list + sizeof(struct scsi_read_defect_data_hdr_10)); num_returned = returned_length / sizeof(struct scsi_defect_desc_block); fprintf(stderr, "Got %d defect", num_returned); if ((lists_specified == 0) || (num_returned == 0)) { fprintf(stderr, "s.\n"); break; } else if (num_returned == 1) fprintf(stderr, ":\n"); else fprintf(stderr, "s:\n"); for (i = 0; i < num_returned; i++) fprintf(stdout, "%u\n", scsi_4btoul(dlist[i].address)); break; } default: fprintf(stderr, "Unknown defect format %d\n", returned_format & SRDDH10_DLIST_FORMAT_MASK); error = 1; break; } defect_bailout: if (defect_list != NULL) free(defect_list); if (ccb != NULL) cam_freeccb(ccb); return(error); } #if 0 void reassignblocks(struct cam_device *device, u_int32_t *blocks, int num_blocks) { union ccb *ccb; ccb = cam_getccb(device); cam_freeccb(ccb); } #endif void mode_sense(struct cam_device *device, int mode_page, int page_control, int dbd, int retry_count, int timeout, u_int8_t *data, int datalen) { union ccb *ccb; int retval; ccb = cam_getccb(device); if (ccb == NULL) errx(1, "mode_sense: couldn't allocate CCB"); bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); scsi_mode_sense(&ccb->csio, /* retries */ retry_count, /* cbfcnp */ NULL, /* tag_action */ MSG_SIMPLE_Q_TAG, /* dbd */ dbd, /* page_code */ page_control << 6, /* page */ mode_page, /* param_buf */ data, /* param_len */ datalen, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 5000); if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); cam_close_device(device); if (retval < 0) err(1, "error sending mode sense command"); else errx(1, "error sending mode sense command"); } cam_freeccb(ccb); } void mode_select(struct cam_device *device, int save_pages, int retry_count, int timeout, u_int8_t *data, int datalen) { union ccb *ccb; int retval; ccb = cam_getccb(device); if (ccb == NULL) errx(1, "mode_select: couldn't allocate CCB"); bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); scsi_mode_select(&ccb->csio, /* retries */ retry_count, /* cbfcnp */ NULL, /* tag_action */ MSG_SIMPLE_Q_TAG, /* scsi_page_fmt */ 1, /* save_pages */ save_pages, /* param_buf */ data, /* param_len */ datalen, /* sense_len */ SSD_FULL_SIZE, /* timeout */ timeout ? timeout : 5000); if (arglist & CAM_ARG_ERR_RECOVER) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; /* Disable freezing the device queue */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } cam_freeccb(ccb); cam_close_device(device); if (retval < 0) err(1, "error sending mode select command"); else errx(1, "error sending mode select command"); } cam_freeccb(ccb); } void modepage(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { int c, mode_page = -1, page_control = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c) { case 'd': arglist |= CAM_ARG_DBD; break; case 'e': arglist |= CAM_ARG_MODE_EDIT; break; case 'm': mode_page = strtol(optarg, NULL, 0); if (mode_page < 0) errx(1, "invalid mode page %d", mode_page); break; case 'P': page_control = strtol(optarg, NULL, 0); if ((page_control < 0) || (page_control > 3)) errx(1, "invalid page control field %d", page_control); arglist |= CAM_ARG_PAGE_CNTL; break; default: break; } } if (mode_page == -1) errx(1, "you must specify a mode page!"); mode_edit(device, mode_page, page_control, arglist & CAM_ARG_DBD, arglist & CAM_ARG_MODE_EDIT, retry_count, timeout); } static int scsicmd(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout) { union ccb *ccb; u_int32_t flags = CAM_DIR_NONE; u_int8_t *data_ptr = NULL; u_int8_t cdb[20]; struct get_hook hook; int c, data_bytes = 0; int cdb_len = 0; char *datastr = NULL, *tstr; int error = 0; int fd_data = 0; int retval; ccb = cam_getccb(device); if (ccb == NULL) { warnx("scsicmd: error allocating ccb"); return(1); } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c) { case 'c': tstr = optarg; while (isspace(*tstr) && (*tstr != '\0')) tstr++; hook.argc = argc - optind; hook.argv = argv + optind; hook.got = 0; cdb_len = buff_encode_visit(cdb, sizeof(cdb), tstr, iget, &hook); /* * Increment optind by the number of arguments the * encoding routine processed. After each call to * getopt(3), optind points to the argument that * getopt should process _next_. In this case, * that means it points to the first command string * argument, if there is one. Once we increment * this, it should point to either the next command * line argument, or it should be past the end of * the list. */ optind += hook.got; break; case 'i': if (arglist & CAM_ARG_CMD_OUT) { warnx("command must either be " "read or write, not both"); error = 1; goto scsicmd_bailout; } arglist |= CAM_ARG_CMD_IN; flags = CAM_DIR_IN; data_bytes = strtol(optarg, NULL, 0); if (data_bytes <= 0) { warnx("invalid number of input bytes %d", data_bytes); error = 1; goto scsicmd_bailout; } hook.argc = argc - optind; hook.argv = argv + optind; hook.got = 0; optind++; datastr = cget(&hook, NULL); /* * If the user supplied "-" instead of a format, he * wants the data to be written to stdout. */ if ((datastr != NULL) && (datastr[0] == '-')) fd_data = 1; data_ptr = (u_int8_t *)malloc(data_bytes); break; case 'o': if (arglist & CAM_ARG_CMD_IN) { warnx("command must either be " "read or write, not both"); error = 1; goto scsicmd_bailout; } arglist |= CAM_ARG_CMD_OUT; flags = CAM_DIR_OUT; data_bytes = strtol(optarg, NULL, 0); if (data_bytes <= 0) { warnx("invalid number of output bytes %d", data_bytes); error = 1; goto scsicmd_bailout; } hook.argc = argc - optind; hook.argv = argv + optind; hook.got = 0; datastr = cget(&hook, NULL); data_ptr = (u_int8_t *)malloc(data_bytes); /* * If the user supplied "-" instead of a format, he * wants the data to be read from stdin. */ if ((datastr != NULL) && (datastr[0] == '-')) fd_data = 1; else buff_encode_visit(data_ptr, data_bytes, datastr, iget, &hook); optind += hook.got; break; default: break; } } /* * If fd_data is set, and we're writing to the device, we need to * read the data the user wants written from stdin. */ if ((fd_data == 1) && (arglist & CAM_ARG_CMD_OUT)) { size_t amt_read; int amt_to_read = data_bytes; u_int8_t *buf_ptr = data_ptr; for (amt_read = 0; amt_to_read > 0; amt_read = read(0, buf_ptr, amt_to_read)) { if (amt_read == -1) { warn("error reading data from stdin"); error = 1; goto scsicmd_bailout; } amt_to_read -= amt_read; buf_ptr += amt_read; } } if (arglist & CAM_ARG_ERR_RECOVER) flags |= CAM_PASS_ERR_RECOVER; /* Disable freezing the device queue */ flags |= CAM_DEV_QFRZDIS; /* * This is taken from the SCSI-3 draft spec. * (T10/1157D revision 0.3) * The top 3 bits of an opcode are the group code. The next 5 bits * are the command code. * Group 0: six byte commands * Group 1: ten byte commands * Group 2: ten byte commands * Group 3: reserved * Group 4: sixteen byte commands * Group 5: twelve byte commands * Group 6: vendor specific * Group 7: vendor specific */ switch((cdb[0] >> 5) & 0x7) { case 0: cdb_len = 6; break; case 1: case 2: cdb_len = 10; break; case 3: case 6: case 7: /* computed by buff_encode_visit */ break; case 4: cdb_len = 16; break; case 5: cdb_len = 12; break; } /* * We should probably use csio_build_visit or something like that * here, but it's easier to encode arguments as you go. The * alternative would be skipping the CDB argument and then encoding * it here, since we've got the data buffer argument by now. */ bcopy(cdb, &ccb->csio.cdb_io.cdb_bytes, cdb_len); cam_fill_csio(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*flags*/ flags, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*data_ptr*/ data_ptr, /*dxfer_len*/ data_bytes, /*sense_len*/ SSD_FULL_SIZE, /*cdb_len*/ cdb_len, /*timeout*/ timeout ? timeout : 5000); if (((retval = cam_send_ccb(device, ccb)) < 0) || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) { if (retval < 0) warn("error sending command"); else warnx("error sending command"); if (arglist & CAM_ARG_VERBOSE) { if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) scsi_sense_print(device, &ccb->csio, stderr); else fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); } error = 1; goto scsicmd_bailout; } if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) && (arglist & CAM_ARG_CMD_IN) && (data_bytes > 0)) { if (fd_data == 0) { buff_decode_visit(data_ptr, data_bytes, datastr, arg_put, NULL); fprintf(stdout, "\n"); } else { size_t amt_written; int amt_to_write = data_bytes; u_int8_t *buf_ptr = data_ptr; for (amt_written = 0; (amt_to_write > 0) && (amt_written =write(1, buf_ptr,amt_to_write))> 0;){ amt_to_write -= amt_written; buf_ptr += amt_written; } if (amt_written == -1) { warn("error writing data to stdout"); error = 1; goto scsicmd_bailout; } else if ((amt_written == 0) && (amt_to_write > 0)) { warnx("only wrote %u bytes out of %u", data_bytes - amt_to_write, data_bytes); } } } scsicmd_bailout: if ((data_bytes > 0) && (data_ptr != NULL)) free(data_ptr); cam_freeccb(ccb); return(error); } static int camdebug(int argc, char **argv, char *combinedopt) { int c, fd; int bus = -1, target = -1, lun = -1; char *tstr, *tmpstr = NULL; union ccb ccb; int error = 0; bzero(&ccb, sizeof(union ccb)); while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c) { case 'I': arglist |= CAM_ARG_DEBUG_INFO; ccb.cdbg.flags |= CAM_DEBUG_INFO; break; case 'S': arglist |= CAM_ARG_DEBUG_SUBTRACE; ccb.cdbg.flags |= CAM_DEBUG_SUBTRACE; break; case 'T': arglist |= CAM_ARG_DEBUG_TRACE; ccb.cdbg.flags |= CAM_DEBUG_TRACE; break; case 'c': arglist |= CAM_ARG_DEBUG_CDB; ccb.cdbg.flags |= CAM_DEBUG_CDB; break; default: break; } } if ((fd = open(XPT_DEVICE, O_RDWR)) < 0) { warnx("error opening transport layer device %s", XPT_DEVICE); warn("%s", XPT_DEVICE); return(1); } argc -= optind; argv += optind; if (argc <= 0) { warnx("you must specify \"off\", \"all\" or a bus,"); warnx("bus:target, or bus:target:lun"); close(fd); return(1); } tstr = *argv; while (isspace(*tstr) && (*tstr != '\0')) tstr++; if (strncmp(tstr, "off", 3) == 0) { ccb.cdbg.flags = CAM_DEBUG_NONE; arglist &= ~(CAM_ARG_DEBUG_INFO|CAM_ARG_DEBUG_TRACE| CAM_ARG_DEBUG_SUBTRACE); } else if (strncmp(tstr, "all", 3) != 0) { tmpstr = (char *)strtok(tstr, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')){ bus = strtol(tmpstr, NULL, 0); arglist |= CAM_ARG_BUS; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')){ target = strtol(tmpstr, NULL, 0); arglist |= CAM_ARG_TARGET; tmpstr = (char *)strtok(NULL, ":"); if ((tmpstr != NULL) && (*tmpstr != '\0')){ lun = strtol(tmpstr, NULL, 0); arglist |= CAM_ARG_LUN; } } } else { error = 1; warnx("you must specify \"all\", \"off\", or a bus,"); warnx("bus:target, or bus:target:lun to debug"); } } if (error == 0) { ccb.ccb_h.func_code = XPT_DEBUG; ccb.ccb_h.path_id = bus; ccb.ccb_h.target_id = target; ccb.ccb_h.target_lun = lun; if (ioctl(fd, CAMIOCOMMAND, &ccb) == -1) { warn("CAMIOCOMMAND ioctl failed"); error = 1; } if (error == 0) { if ((ccb.ccb_h.status & CAM_STATUS_MASK) == CAM_FUNC_NOTAVAIL) { warnx("CAM debugging not available"); warnx("you need to put options CAMDEBUG in" " your kernel config file!"); error = 1; } else if ((ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_DEBUG CCB failed with status %#x", ccb.ccb_h.status); error = 1; } else { if (ccb.cdbg.flags == CAM_DEBUG_NONE) { fprintf(stderr, "Debugging turned off\n"); } else { fprintf(stderr, "Debugging enabled for " "%d:%d:%d\n", bus, target, lun); } } } close(fd); } return(error); } static int tagcontrol(struct cam_device *device, int argc, char **argv, char *combinedopt) { int c; union ccb *ccb; int numtags = -1; int retval = 0; int quiet = 0; char pathstr[1024]; ccb = cam_getccb(device); if (ccb == NULL) { warnx("tagcontrol: error allocating ccb"); return(1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c) { case 'N': numtags = strtol(optarg, NULL, 0); if (numtags < 0) { warnx("tag count %d is < 0", numtags); retval = 1; goto tagcontrol_bailout; } break; case 'q': quiet++; break; default: break; } } cam_path_string(device, pathstr, sizeof(pathstr)); if (numtags >= 0) { bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_relsim) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_REL_SIMQ; ccb->crs.release_flags = RELSIM_ADJUST_OPENINGS; ccb->crs.openings = numtags; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_REL_SIMQ CCB"); retval = 1; goto tagcontrol_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_REL_SIMQ CCB failed, status %#x", ccb->ccb_h.status); retval = 1; goto tagcontrol_bailout; } if (quiet == 0) fprintf(stdout, "%stagged openings now %d\n", pathstr, ccb->crs.openings); } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_getdev) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_GDEV_STATS; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_GDEV_STATS CCB"); retval = 1; goto tagcontrol_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_GDEV_STATS CCB failed, status %#x", ccb->ccb_h.status); retval = 1; goto tagcontrol_bailout; } if (arglist & CAM_ARG_VERBOSE) { fprintf(stdout, "%s", pathstr); fprintf(stdout, "dev_openings %d\n", ccb->cgds.dev_openings); fprintf(stdout, "%s", pathstr); fprintf(stdout, "dev_active %d\n", ccb->cgds.dev_active); fprintf(stdout, "%s", pathstr); fprintf(stdout, "devq_openings %d\n", ccb->cgds.devq_openings); fprintf(stdout, "%s", pathstr); fprintf(stdout, "devq_queued %d\n", ccb->cgds.devq_queued); fprintf(stdout, "%s", pathstr); fprintf(stdout, "held %d\n", ccb->cgds.held); fprintf(stdout, "%s", pathstr); fprintf(stdout, "mintags %d\n", ccb->cgds.mintags); fprintf(stdout, "%s", pathstr); fprintf(stdout, "maxtags %d\n", ccb->cgds.maxtags); } else { if (quiet == 0) { fprintf(stdout, "%s", pathstr); fprintf(stdout, "device openings: "); } fprintf(stdout, "%d\n", ccb->cgds.dev_openings + ccb->cgds.dev_active); } tagcontrol_bailout: cam_freeccb(ccb); return(retval); } static void cts_print(struct cam_device *device, struct ccb_trans_settings *cts) { char pathstr[1024]; cam_path_string(device, pathstr, sizeof(pathstr)); if ((cts->valid & CCB_TRANS_SYNC_RATE_VALID) != 0) { fprintf(stdout, "%ssync parameter: %d\n", pathstr, cts->sync_period); if (cts->sync_offset != 0) { u_int freq; freq = scsi_calc_syncsrate(cts->sync_period); fprintf(stdout, "%sfrequencey: %d.%03dMHz\n", pathstr, freq / 1000, freq % 1000); } } if (cts->valid & CCB_TRANS_SYNC_OFFSET_VALID) fprintf(stdout, "%soffset: %d\n", pathstr, cts->sync_offset); if (cts->valid & CCB_TRANS_BUS_WIDTH_VALID) fprintf(stdout, "%sbus width: %d bits\n", pathstr, (0x01 << cts->bus_width) * 8); if (cts->valid & CCB_TRANS_DISC_VALID) fprintf(stdout, "%sdisconnection is %s\n", pathstr, (cts->flags & CCB_TRANS_DISC_ENB) ? "enabled" : "disabled"); if (cts->valid & CCB_TRANS_TQ_VALID) fprintf(stdout, "%stagged queueing is %s\n", pathstr, (cts->flags & CCB_TRANS_TAG_ENB) ? "enabled" : "disabled"); } /* * Get a path inquiry CCB for the specified device. */ static int get_cpi(struct cam_device *device, struct ccb_pathinq *cpi) { union ccb *ccb; int retval = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("get_cpi: couldn't allocate CCB"); return(1); } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_PATH_INQ; if (cam_send_ccb(device, ccb) < 0) { warn("get_cpi: error sending Path Inquiry CCB"); if (arglist & CAM_ARG_VERBOSE) fprintf(stderr, "CAM status is %#x\n", ccb->ccb_h.status); retval = 1; goto get_cpi_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (arglist & CAM_ARG_VERBOSE) fprintf(stderr, "get_cpi: CAM status is %#x\n", ccb->ccb_h.status); retval = 1; goto get_cpi_bailout; } bcopy(&ccb->cpi, cpi, sizeof(struct ccb_pathinq)); get_cpi_bailout: cam_freeccb(ccb); return(retval); } static void cpi_print(struct ccb_pathinq *cpi) { char adapter_str[1024]; int i; snprintf(adapter_str, sizeof(adapter_str), "%s%d:", cpi->dev_name, cpi->unit_number); fprintf(stdout, "%s SIM/HBA version: %d\n", adapter_str, cpi->version_num); for (i = 1; i < 0xff; i = i << 1) { char *str; if ((i & cpi->hba_inquiry) == 0) continue; fprintf(stdout, "%s supports ", adapter_str); switch(i) { case PI_MDP_ABLE: str = "MDP message"; break; case PI_WIDE_32: str = "32 bit wide SCSI"; break; case PI_WIDE_16: str = "16 bit wide SCSI"; break; case PI_SDTR_ABLE: str = "SDTR message"; break; case PI_LINKED_CDB: str = "linked CDBs"; break; case PI_TAG_ABLE: str = "tag queue messages"; break; case PI_SOFT_RST: str = "soft reset alternative"; break; default: str = "unknown PI bit set"; break; } fprintf(stdout, "%s\n", str); } for (i = 1; i < 0xff; i = i << 1) { char *str; if ((i & cpi->hba_misc) == 0) continue; fprintf(stdout, "%s ", adapter_str); switch(i) { case PIM_SCANHILO: str = "bus scans from high ID to low ID"; break; case PIM_NOREMOVE: str = "removable devices not included in scan"; break; case PIM_NOINITIATOR: str = "initiator role not supported"; break; case PIM_NOBUSRESET: str = "user has disabled initial BUS RESET or" " controller is in target/mixed mode"; break; default: str = "unknown PIM bit set"; break; } fprintf(stdout, "%s\n", str); } for (i = 1; i < 0xff; i = i << 1) { char *str; if ((i & cpi->target_sprt) == 0) continue; fprintf(stdout, "%s supports ", adapter_str); switch(i) { case PIT_PROCESSOR: str = "target mode processor mode"; break; case PIT_PHASE: str = "target mode phase cog. mode"; break; case PIT_DISCONNECT: str = "disconnects in target mode"; break; case PIT_TERM_IO: str = "terminate I/O message in target mode"; break; case PIT_GRP_6: str = "group 6 commands in target mode"; break; case PIT_GRP_7: str = "group 7 commands in target mode"; break; default: str = "unknown PIT bit set"; break; } fprintf(stdout, "%s\n", str); } fprintf(stdout, "%s HBA engine count: %d\n", adapter_str, cpi->hba_eng_cnt); fprintf(stdout, "%s maximum target: %d\n", adapter_str, cpi->max_target); fprintf(stdout, "%s maximum LUN: %d\n", adapter_str, cpi->max_lun); fprintf(stdout, "%s highest path ID in subsystem: %d\n", adapter_str, cpi->hpath_id); fprintf(stdout, "%s SIM vendor: %s\n", adapter_str, cpi->sim_vid); fprintf(stdout, "%s HBA vendor: %s\n", adapter_str, cpi->hba_vid); fprintf(stdout, "%s bus ID: %d\n", adapter_str, cpi->bus_id); fprintf(stdout, "%s base transfer speed: ", adapter_str); if (cpi->base_transfer_speed > 1000) fprintf(stdout, "%d.%03dMB/sec\n", cpi->base_transfer_speed / 1000, cpi->base_transfer_speed % 1000); else fprintf(stdout, "%dKB/sec\n", (cpi->base_transfer_speed % 1000) * 1000); } static int get_print_cts(struct cam_device *device, int user_settings, int quiet, struct ccb_trans_settings *cts) { int retval; union ccb *ccb; retval = 0; ccb = cam_getccb(device); if (ccb == NULL) { warnx("get_print_cts: error allocating ccb"); return(1); } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr)); ccb->ccb_h.func_code = XPT_GET_TRAN_SETTINGS; if (user_settings == 0) ccb->cts.flags = CCB_TRANS_CURRENT_SETTINGS; else ccb->cts.flags = CCB_TRANS_USER_SETTINGS; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_GET_TRAN_SETTINGS CCB"); retval = 1; goto get_print_cts_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_GET_TRANS_SETTINGS CCB failed, status %#x", ccb->ccb_h.status); retval = 1; goto get_print_cts_bailout; } if (quiet == 0) cts_print(device, &ccb->cts); if (cts != NULL) bcopy(&ccb->cts, cts, sizeof(struct ccb_trans_settings)); get_print_cts_bailout: cam_freeccb(ccb); return(retval); } static int ratecontrol(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt) { int c; union ccb *ccb; int user_settings = 0; int retval = 0; int disc_enable = -1, tag_enable = -1; int offset = -1; double syncrate = -1; int bus_width = -1; int quiet = 0; int change_settings = 0, send_tur = 0; struct ccb_pathinq cpi; ccb = cam_getccb(device); if (ccb == NULL) { warnx("ratecontrol: error allocating ccb"); return(1); } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch(c){ case 'a': send_tur = 1; break; case 'c': user_settings = 0; break; case 'D': if (strncasecmp(optarg, "enable", 6) == 0) disc_enable = 1; else if (strncasecmp(optarg, "disable", 7) == 0) disc_enable = 0; else { warnx("-D argument \"%s\" is unknown", optarg); retval = 1; goto ratecontrol_bailout; } change_settings = 1; break; case 'O': offset = strtol(optarg, NULL, 0); if (offset < 0) { warnx("offset value %d is < 0", offset); retval = 1; goto ratecontrol_bailout; } change_settings = 1; break; case 'q': quiet++; break; case 'R': syncrate = atof(optarg); if (syncrate < 0) { warnx("sync rate %f is < 0", syncrate); retval = 1; goto ratecontrol_bailout; } change_settings = 1; break; case 'T': if (strncasecmp(optarg, "enable", 6) == 0) tag_enable = 1; else if (strncasecmp(optarg, "disable", 7) == 0) tag_enable = 0; else { warnx("-T argument \"%s\" is unknown", optarg); retval = 1; goto ratecontrol_bailout; } change_settings = 1; break; case 'U': user_settings = 1; break; case 'W': bus_width = strtol(optarg, NULL, 0); if (bus_width < 0) { warnx("bus width %d is < 0", bus_width); retval = 1; goto ratecontrol_bailout; } change_settings = 1; break; default: break; } } bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_pathinq) - sizeof(struct ccb_hdr)); /* * Grab path inquiry information, so we can determine whether * or not the initiator is capable of the things that the user * requests. */ ccb->ccb_h.func_code = XPT_PATH_INQ; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_PATH_INQ CCB"); retval = 1; goto ratecontrol_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_PATH_INQ CCB failed, status %#x", ccb->ccb_h.status); retval = 1; goto ratecontrol_bailout; } bcopy(&ccb->cpi, &cpi, sizeof(struct ccb_pathinq)); bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_trans_settings) - sizeof(struct ccb_hdr)); if (quiet == 0) fprintf(stdout, "Current Parameters:\n"); retval = get_print_cts(device, user_settings, quiet, &ccb->cts); if (retval != 0) goto ratecontrol_bailout; if (arglist & CAM_ARG_VERBOSE) cpi_print(&cpi); if (change_settings) { if (disc_enable != -1) { ccb->cts.valid |= CCB_TRANS_DISC_VALID; if (disc_enable == 0) ccb->cts.flags &= ~CCB_TRANS_DISC_ENB; else ccb->cts.flags |= CCB_TRANS_DISC_ENB; } else ccb->cts.valid &= ~CCB_TRANS_DISC_VALID; if (tag_enable != -1) { if ((cpi.hba_inquiry & PI_TAG_ABLE) == 0) { warnx("HBA does not support tagged queueing, " "so you cannot modify tag settings"); retval = 1; goto ratecontrol_bailout; } ccb->cts.valid |= CCB_TRANS_TQ_VALID; if (tag_enable == 0) ccb->cts.flags &= ~CCB_TRANS_TAG_ENB; else ccb->cts.flags |= CCB_TRANS_TAG_ENB; } else ccb->cts.valid &= ~CCB_TRANS_TQ_VALID; if (offset != -1) { if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) { warnx("HBA at %s%d is not cable of changing " "offset", cpi.dev_name, cpi.unit_number); retval = 1; goto ratecontrol_bailout; } ccb->cts.valid |= CCB_TRANS_SYNC_OFFSET_VALID; ccb->cts.sync_offset = offset; } else ccb->cts.valid &= ~CCB_TRANS_SYNC_OFFSET_VALID; if (syncrate != -1) { int prelim_sync_period; u_int freq; if ((cpi.hba_inquiry & PI_SDTR_ABLE) == 0) { warnx("HBA at %s%d is not cable of changing " "transfer rates", cpi.dev_name, cpi.unit_number); retval = 1; goto ratecontrol_bailout; } ccb->cts.valid |= CCB_TRANS_SYNC_RATE_VALID; /* * The sync rate the user gives us is in MHz. * We need to translate it into KHz for this * calculation. */ syncrate *= 1000; /* * Next, we calculate a "preliminary" sync period * in tenths of a nanosecond. */ if (syncrate == 0) prelim_sync_period = 0; else prelim_sync_period = 10000000 / syncrate; ccb->cts.sync_period = scsi_calc_syncparam(prelim_sync_period); freq = scsi_calc_syncsrate(ccb->cts.sync_period); } else ccb->cts.valid &= ~CCB_TRANS_SYNC_RATE_VALID; /* * The bus_width argument goes like this: * 0 == 8 bit * 1 == 16 bit * 2 == 32 bit * Therefore, if you shift the number of bits given on the * command line right by 4, you should get the correct * number. */ if (bus_width != -1) { /* * We might as well validate things here with a * decipherable error message, rather than what * will probably be an indecipherable error message * by the time it gets back to us. */ if ((bus_width == 16) && ((cpi.hba_inquiry & PI_WIDE_16) == 0)) { warnx("HBA does not support 16 bit bus width"); retval = 1; goto ratecontrol_bailout; } else if ((bus_width == 32) && ((cpi.hba_inquiry & PI_WIDE_32) == 0)) { warnx("HBA does not support 32 bit bus width"); retval = 1; goto ratecontrol_bailout; } else if ((bus_width != 8) && (bus_width != 16) && (bus_width != 32)) { warnx("Invalid bus width %d", bus_width); retval = 1; goto ratecontrol_bailout; } ccb->cts.valid |= CCB_TRANS_BUS_WIDTH_VALID; ccb->cts.bus_width = bus_width >> 4; } else ccb->cts.valid &= ~CCB_TRANS_BUS_WIDTH_VALID; ccb->ccb_h.func_code = XPT_SET_TRAN_SETTINGS; if (cam_send_ccb(device, ccb) < 0) { perror("error sending XPT_SET_TRAN_SETTINGS CCB"); retval = 1; goto ratecontrol_bailout; } if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { warnx("XPT_SET_TRANS_SETTINGS CCB failed, status %#x", ccb->ccb_h.status); retval = 1; goto ratecontrol_bailout; } } if (send_tur) { retval = testunitready(device, retry_count, timeout, (arglist & CAM_ARG_VERBOSE) ? 0 : 1); /* * If the TUR didn't succeed, just bail. */ if (retval != 0) { if (quiet == 0) fprintf(stderr, "Test Unit Ready failed\n"); goto ratecontrol_bailout; } /* * If the user wants things quiet, there's no sense in * getting the transfer settings, if we're not going * to print them. */ if (quiet != 0) goto ratecontrol_bailout; fprintf(stdout, "New Parameters:\n"); retval = get_print_cts(device, user_settings, 0, NULL); } ratecontrol_bailout: cam_freeccb(ccb); return(retval); } void usage(int verbose) { fprintf(stderr, "usage: camcontrol [device id][generic args][command args]\n" " camcontrol devlist [-v]\n" " camcontrol periphlist [dev_id][-n dev_name] [-u unit]\n" " camcontrol tur [dev_id][generic args]\n" " camcontrol inquiry [dev_id][generic args] [-D] [-S] [-R]\n" " camcontrol start [dev_id][generic args]\n" " camcontrol stop [dev_id][generic args]\n" " camcontrol eject [dev_id][generic args]\n" " camcontrol rescan \n" " camcontrol reset \n" " camcontrol defects [dev_id][generic args] <-f format> [-P][-G]\n" " camcontrol modepage [dev_id][generic args] <-m page> [-P pagectl]\n" " [-e][-d]\n" " camcontrol cmd [dev_id][generic args] <-c cmd [args]>\n" " [-i len fmt|-o len fmt [args]]\n" " camcontrol debug [-I][-T][-S][-c] \n" " camcontrol tags [dev_id][generic args] [-N tags] [-q] [-v]\n" " camcontrol negotiate [dev_id][generic args] [-a][-c]\n" " [-D ][-O offset][-q]\n" " [-R syncrate][-v][-T ]\n" " [-U][-W bus_width]\n" " camcontrol help\n"); if (!verbose) return; fprintf(stderr, "Specify one of the following options:\n" "devlist list all CAM devices\n" "periphlist list all CAM peripheral drivers attached to a device\n" "tur send a test unit ready to the named device\n" "inquiry send a SCSI inquiry command to the named device\n" "start send a Start Unit command to the device\n" "stop send a Stop Unit command to the device\n" "eject send a Stop Unit command to the device with the eject bit set\n" "rescan rescan the given bus, or bus:target:lun\n" "reset reset the given bus, or bus:target:lun\n" "defects read the defect list of the specified device\n" "modepage display or edit (-e) the given mode page\n" "cmd send the given scsi command, may need -i or -o as well\n" "debug turn debugging on/off for a bus, target, or lun, or all devices\n" "tags report or set the number of transaction slots for a device\n" "negotiate report or set device negotiation parameters\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" "bus:target:lun specify the bus, target and lun\n" "deviceUNIT specify the device name, like \"da4\" or \"cd2\"\n" "Generic arguments:\n" "-v be verbose, print out sense information\n" "-t timeout command timeout in seconds, overrides default timeout\n" "-n dev_name specify device name (default is %s)\n" "-u unit specify unit number (default is %d)\n" "-E have the kernel attempt to perform SCSI error recovery\n" "-C count specify the SCSI command retry count (needs -E to work)\n" "modepage arguments:\n" "-m page specify the mode page to view or edit\n" "-e edit the specified mode page\n" "-d disable block descriptors for mode sense\n" "-P pgctl page control field 0-3\n" "defects arguments:\n" "-f format specify defect list format (block, bfi or phys)\n" "-G get the grown defect list\n" "-P get the permanant defect list\n" "inquiry arguments:\n" "-D get the standard inquiry data\n" "-S get the serial number\n" "-R get the transfer rate, etc.\n" "cmd arguments:\n" "-c cdb [args] specify the SCSI CDB\n" "-i len fmt specify input data and input data format\n" "-o len fmt [args] specify output data and output data fmt\n" "debug arguments:\n" "-I CAM_DEBUG_INFO -- scsi commands, errors, data\n" "-T CAM_DEBUG_TRACE -- routine flow tracking\n" "-S CAM_DEBUG_SUBTRACE -- internal routine command flow\n" "-c CAM_DEBUG_CDB -- print out SCSI CDBs only\n" "tags arguments:\n" "-N tags specify the number of tags to use for this device\n" "-q be quiet, don't report the number of tags\n" "-v report a number of tag-related parameters\n" "negotiate arguments:\n" "-a send a test unit ready after negotiation\n" "-c report/set current negotiation settings\n" "-D \"enable\" or \"disable\" disconnection\n" "-O offset set command delay offset\n" "-q be quiet, don't report anything\n" "-R syncrate synchronization rate in MHz\n" "-T \"enable\" or \"disable\" tagged queueing\n" "-U report/set user negotiation settings\n" "-W bus_width set the bus width in bits (8, 16 or 32)\n" "-v also print a Path Inquiry CCB for the controller\n", DEFAULT_DEVICE, DEFAULT_UNIT); } int main(int argc, char **argv) { int c; char *device = NULL; int unit = 0; struct cam_device *cam_dev = NULL; int timeout = 0, retry_count = 1; camcontrol_optret optreturn; char *tstr; char *mainopt = "C:En:t:u:v"; char *subopt = NULL; char combinedopt[256]; int error = 0, optstart = 2; int devopen = 1; arglist = CAM_ARG_NONE; if (argc < 2) { usage(0); exit(1); } /* * Get the base option. */ optreturn = getoption(argv[1], &arglist, &subopt); if (optreturn == CC_OR_AMBIGUOUS) { warnx("ambiguous option %s", argv[1]); usage(0); exit(1); } else if (optreturn == CC_OR_NOT_FOUND) { warnx("option %s not found", argv[1]); usage(0); exit(1); } /* * Ahh, getopt(3) is a pain. * * This is a gross hack. There really aren't many other good * options (excuse the pun) for parsing options in a situation like * this. getopt is kinda braindead, so you end up having to run * through the options twice, and give each invocation of getopt * the option string for the other invocation. * * You would think that you could just have two groups of options. * The first group would get parsed by the first invocation of * getopt, and the second group would get parsed by the second * invocation of getopt. It doesn't quite work out that way. When * the first invocation of getopt finishes, it leaves optind pointing * to the argument _after_ the first argument in the second group. * So when the second invocation of getopt comes around, it doesn't * recognize the first argument it gets and then bails out. * * A nice alternative would be to have a flag for getopt that says * "just keep parsing arguments even when you encounter an unknown * argument", but there isn't one. So there's no real clean way to * easily parse two sets of arguments without having one invocation * of getopt know about the other. * * Without this hack, the first invocation of getopt would work as * long as the generic arguments are first, but the second invocation * (in the subfunction) would fail in one of two ways. In the case * where you don't set optreset, it would fail because optind may be * pointing to the argument after the one it should be pointing at. * In the case where you do set optreset, and reset optind, it would * fail because getopt would run into the first set of options, which * it doesn't understand. * * All of this would "sort of" work if you could somehow figure out * whether optind had been incremented one option too far. The * mechanics of that, however, are more daunting than just giving * both invocations all of the expect options for either invocation. * * Needless to say, I wouldn't mind if someone invented a better * (non-GPL!) command line parsing interface than getopt. I * wouldn't mind if someone added more knobs to getopt to make it * work better. Who knows, I may talk myself into doing it someday, * if the standards weenies let me. As it is, it just leads to * hackery like this and causes people to avoid it in some cases. * * KDM, September 8th, 1998 */ if (subopt != NULL) sprintf(combinedopt, "%s%s", mainopt, subopt); else sprintf(combinedopt, "%s", mainopt); /* * For these options we do not parse optional device arguments and * we do not open a passthrough device. */ if (((arglist & CAM_ARG_OPT_MASK) == CAM_ARG_RESCAN) || ((arglist & CAM_ARG_OPT_MASK) == CAM_ARG_RESET) || ((arglist & CAM_ARG_OPT_MASK) == CAM_ARG_DEVTREE) || ((arglist & CAM_ARG_OPT_MASK) == CAM_ARG_USAGE) || ((arglist & CAM_ARG_OPT_MASK) == CAM_ARG_DEBUG)) devopen = 0; if ((devopen == 1) && (argc > 2 && argv[2][0] != '-')) { char name[30]; int rv; /* * First catch people who try to do things like: * camcontrol tur /dev/rsd0.ctl * camcontrol doesn't take device nodes as arguments. */ if (argv[2][0] == '/') { warnx("%s is not a valid device identifier", argv[2]); errx(1, "please read the camcontrol(8) man page"); } else if (isdigit(argv[2][0])) { /* device specified as bus:target[:lun] */ rv = parse_btl(argv[2], &bus, &target, &lun, &arglist); if (rv < 2) errx(1, "numeric device specification must " "be either bus:target, or " "bus:target:lun"); optstart++; } else { if (cam_get_device(argv[2], name, sizeof name, &unit) == -1) errx(1, "%s", cam_errbuf); device = strdup(name); arglist |= CAM_ARG_DEVICE | CAM_ARG_UNIT; optstart++; } } /* * Start getopt processing at argv[2/3], since we've already * accepted argv[1..2] as the command name, and as a possible * device name. */ optind = optstart; /* * Now we run through the argument list looking for generic * options, and ignoring options that possibly belong to * subfunctions. */ while ((c = getopt(argc, argv, combinedopt))!= -1){ switch(c) { case 'C': retry_count = strtol(optarg, NULL, 0); if (retry_count < 0) errx(1, "retry count %d is < 0", retry_count); arglist |= CAM_ARG_RETRIES; break; case 'E': arglist |= CAM_ARG_ERR_RECOVER; break; case 'n': arglist |= CAM_ARG_DEVICE; tstr = optarg; while (isspace(*tstr) && (*tstr != '\0')) tstr++; device = (char *)strdup(tstr); break; case 't': timeout = strtol(optarg, NULL, 0); if (timeout < 0) errx(1, "invalid timeout %d", timeout); /* Convert the timeout from seconds to ms */ timeout *= 1000; arglist |= CAM_ARG_TIMEOUT; break; case 'u': arglist |= CAM_ARG_UNIT; unit = strtol(optarg, NULL, 0); break; case 'v': arglist |= CAM_ARG_VERBOSE; break; default: break; } } if ((arglist & CAM_ARG_DEVICE) == 0) device = (char *)strdup(DEFAULT_DEVICE); if ((arglist & CAM_ARG_UNIT) == 0) unit = DEFAULT_UNIT; /* * For most commands we'll want to open the passthrough device * associated with the specified device. In the case of the rescan * commands, we don't use a passthrough device at all, just the * transport layer device. */ if (devopen == 1) { if ((cam_dev = ((arglist & (CAM_ARG_BUS | CAM_ARG_TARGET))? cam_open_btl(bus, target, lun, O_RDWR, NULL) : cam_open_spec_device(device,unit,O_RDWR,NULL))) == NULL) errx(1,"%s", cam_errbuf); } /* * Reset optind to 2, and reset getopt, so these routines can parse * the arguments again. */ optind = optstart; optreset = 1; switch(arglist & CAM_ARG_OPT_MASK) { case CAM_ARG_DEVLIST: error = getdevlist(cam_dev); break; case CAM_ARG_DEVTREE: error = getdevtree(); break; case CAM_ARG_TUR: error = testunitready(cam_dev, retry_count, timeout, 0); break; case CAM_ARG_INQUIRY: error = scsidoinquiry(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; case CAM_ARG_STARTSTOP: error = scsistart(cam_dev, arglist & CAM_ARG_START_UNIT, arglist & CAM_ARG_EJECT, retry_count, timeout); break; case CAM_ARG_RESCAN: error = dorescan_or_reset(argc, argv, 1); break; case CAM_ARG_RESET: error = dorescan_or_reset(argc, argv, 0); break; case CAM_ARG_READ_DEFECTS: error = readdefects(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; case CAM_ARG_MODE_PAGE: modepage(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; case CAM_ARG_SCSI_CMD: error = scsicmd(cam_dev, argc, argv, combinedopt, retry_count, timeout); break; case CAM_ARG_DEBUG: error = camdebug(argc, argv, combinedopt); break; case CAM_ARG_TAG: error = tagcontrol(cam_dev, argc, argv, combinedopt); break; case CAM_ARG_RATE: error = ratecontrol(cam_dev, retry_count, timeout, argc, argv, combinedopt); break; case CAM_ARG_USAGE: usage(1); break; default: usage(0); error = 1; break; } if (cam_dev != NULL) cam_close_device(cam_dev); exit(error); } diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index 10d4cc8595bf..2844ac47f0e3 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -1,2921 +1,2928 @@ /* * Implementation of Utility functions for all SCSI device types. * * Copyright (c) 1997, 1998 Justin T. Gibbs. * Copyright (c) 1997, 1998 Kenneth D. Merry. * 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, * without modification, immediately at the beginning of the file. * 2. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * 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. * * $FreeBSD$ */ #include #ifdef _KERNEL #include #include #else #include #include #include #endif #include #include #include #include #include #ifndef _KERNEL #include #ifndef FALSE #define FALSE 0 #endif /* FALSE */ #ifndef TRUE #define TRUE 1 #endif /* TRUE */ #define ERESTART -1 /* restart syscall */ #define EJUSTRETURN -2 /* don't modify regs, just return */ #endif /* !_KERNEL */ const char *scsi_sense_key_text[] = { "NO SENSE", "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR", "HARDWARE FAILURE", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT", "BLANK CHECK", "Vendor Specific", "COPY ABORTED", "ABORTED COMMAND", "EQUAL", "VOLUME OVERFLOW", "MISCOMPARE", "RESERVED" }; #if !defined(SCSI_NO_OP_STRINGS) #define D 0x001 #define T 0x002 #define L 0x004 #define P 0x008 #define W 0x010 #define R 0x020 #define S 0x040 #define O 0x080 #define M 0x100 #define C 0x200 #define A 0x400 #define E 0x800 #define ALL 0xFFF /* * WARNING: You must update the num_ops field below for this quirk table * entry if you add more entries. */ static struct op_table_entry plextor_cd_ops[] = { {0xD8, R, "CD-DA READ"} }; static struct scsi_op_quirk_entry scsi_op_quirk_table[] = { { /* * I believe that 0xD8 is the Plextor proprietary command * to read CD-DA data. I'm not sure which Plextor CDROM * models support the command, though. I know for sure * that the 4X, 8X, and 12X models do, and presumably the * 12-20X does. I don't know about any earlier models, * though. If anyone has any more complete information, * feel free to change this quirk entry. */ {T_CDROM, SIP_MEDIA_REMOVABLE, "PLEXTOR", "CD-ROM PX*", "*"}, 1, /* number of vendor-specific opcodes for this entry */ plextor_cd_ops } }; static struct op_table_entry scsi_op_codes[] = { /* * From: ftp://ftp.symbios.com/pub/standards/io/t10/drafts/spc/op-num.txt * Modifications by Kenneth Merry (ken@FreeBSD.ORG) * * Note: order is important in this table, scsi_op_desc() currently * depends on the opcodes in the table being in order to save search time. */ /* * File: OP-NUM.TXT * * SCSI Operation Codes * Numeric Sorted Listing * as of 11/13/96 * * D - DIRECT ACCESS DEVICE (SBC) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- * . L - PRINTER DEVICE (SSC) M = Mandatory * . P - PROCESSOR DEVICE (SPC) O = Optional * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) V = Vendor specific * . . R - CD DEVICE (MMC) R = Reserved * . . S - SCANNER DEVICE (SGC) Z = Obsolete * . . .O - OPTICAL MEMORY DEVICE (SBC) * . . . M - MEDIA CHANGER DEVICE (SMC) * . . . C - COMMUNICATION DEVICE (SSC) * . . . .A - STORAGE ARRAY DEVICE (SCC) * . . . . E - ENCLOSURE SERVICES DEVICE (SES) * OP DTLPWRSOMCAE Description * -- ------------ ---------------------------------------------------- */ /* 00 MMMMMMMMMMMM TEST UNIT READY */ {0x00, ALL, "TEST UNIT READY"}, /* 01 M REWIND */ {0x01, T, "REWIND"}, /* 01 Z V ZO ZO REZERO UNIT */ {0x01, D|L|W|O|M, "REZERO UNIT"}, /* 02 VVVVVV V */ /* 03 MMMMMMMMMMMM REQUEST SENSE */ {0x03, ALL, "REQUEST SENSE"}, /* 04 M O O FORMAT UNIT */ {0x04, D|R|O, "FORMAT UNIT"}, /* 04 O FORMAT MEDIUM */ {0x04, T, "FORMAT MEDIUM"}, /* 04 O FORMAT */ {0x04, L, "FORMAT"}, /* 05 VMVVVV V READ BLOCK LIMITS */ {0x05, T, "READ BLOCK LIMITS"}, /* 06 VVVVVV V */ /* 07 OVV O OV REASSIGN BLOCKS */ {0x07, D|W|O, "REASSIGN BLOCKS"}, /* 07 O INITIALIZE ELEMENT STATUS */ {0x07, M, "INITIALIZE ELEMENT STATUS"}, /* 08 OMV OO OV READ(06) */ {0x08, D|T|W|R|O, "READ(06)"}, /* 08 O RECEIVE */ {0x08, P, "RECEIVE"}, /* 08 M GET MESSAGE(06) */ {0x08, C, "GET MESSAGE(06)"}, /* 09 VVVVVV V */ /* 0A OM O OV WRITE(06) */ {0x0A, D|T|W|O, "WRITE(06)"}, /* 0A M SEND(06) */ {0x0A, P, "SEND(06)"}, /* 0A M SEND MESSAGE(06) */ {0x0A, C, "SEND MESSAGE(06)"}, /* 0A M PRINT */ {0x0A, L, "PRINT"}, /* 0B Z ZO ZV SEEK(06) */ {0x0B, D|W|R|O, "SEEK(06)"}, /* 0B O SLEW AND PRINT */ {0x0B, L, "SLEW AND PRINT"}, /* 0C VVVVVV V */ /* 0D VVVVVV V */ /* 0E VVVVVV V */ /* 0F VOVVVV V READ REVERSE */ {0x0F, T, "READ REVERSE"}, /* 10 VM VVV WRITE FILEMARKS */ {0x10, T, "WRITE FILEMARKS"}, /* 10 O O SYNCHRONIZE BUFFER */ {0x10, L|W, "SYNCHRONIZE BUFFER"}, /* 11 VMVVVV SPACE */ {0x11, T, "SPACE"}, /* 12 MMMMMMMMMMMM INQUIRY */ {0x12, ALL, "INQUIRY"}, /* 13 VOVVVV VERIFY(06) */ {0x13, T, "VERIFY(06)"}, /* 14 VOOVVV RECOVER BUFFERED DATA */ {0x14, T|L, "RECOVER BUFFERED DATA"}, /* 15 OMO OOOOOOOO MODE SELECT(06) */ {0x15, ALL & ~(P), "MODE SELECT(06)"}, /* 16 MMMOMMMM O RESERVE(06) */ {0x16, D|T|L|P|W|R|S|O|E, "RESERVE(06)"}, /* 16 M RESERVE ELEMENT(06) */ {0x16, M, "RESERVE ELEMENT(06)"}, /* 17 MMMOMMMM O RELEASE(06) */ {0x17, ALL & ~(M|C|A), "RELEASE(06)"}, /* 17 M RELEASE ELEMENT(06) */ {0x17, M, "RELEASE ELEMENT(06)"}, /* 18 OOOOOOOO COPY */ {0x18, ALL & ~(M|C|A|E), "COPY"}, /* 19 VMVVVV ERASE */ {0x19, T, "ERASE"}, /* 1A OMO OOOOOOOO MODE SENSE(06) */ {0x1A, ALL & ~(P), "MODE SENSE(06)"}, /* 1B O OM O STOP START UNIT */ {0x1B, D|W|R|O, "STOP START UNIT"}, /* 1B O LOAD UNLOAD */ {0x1B, T, "LOAD UNLOAD"}, /* 1B O SCAN */ {0x1B, S, "SCAN"}, /* 1B O STOP PRINT */ {0x1B, L, "STOP PRINT"}, /* 1C OOOOOOOOOO M RECEIVE DIAGNOSTIC RESULTS */ {0x1C, ALL & ~(A), "RECEIVE DIAGNOSTIC RESULTS"}, /* 1D MMMMMMMMMMMM SEND DIAGNOSTIC */ {0x1D, ALL, "SEND DIAGNOSTIC"}, /* 1E OO OM OO PREVENT ALLOW MEDIUM REMOVAL */ {0x1E, D|T|W|R|O|M, "PREVENT ALLOW MEDIUM REMOVAL"}, /* 1F */ /* 20 V VV V */ /* 21 V VV V */ /* 22 V VV V */ /* 23 V VV V */ /* 24 V VVM SET WINDOW */ {0x24, S, "SET WINDOW"}, /* 25 M M M READ CAPACITY */ {0x25, D|W|O, "READ CAPACITY"}, /* 25 M READ CD RECORDED CAPACITY */ {0x25, R, "READ CD RECORDED CAPACITY"}, /* 25 O GET WINDOW */ {0x25, S, "GET WINDOW"}, /* 26 V VV */ /* 27 V VV */ /* 28 M MMMM READ(10) */ {0x28, D|W|R|S|O, "READ(10)"}, /* 28 O GET MESSAGE(10) */ {0x28, C, "GET MESSAGE(10)"}, /* 29 V VV O READ GENERATION */ {0x29, O, "READ GENERATION"}, /* 2A M MM M WRITE(10) */ {0x2A, D|W|R|O, "WRITE(10)"}, /* 2A O SEND(10) */ {0x2A, S, "SEND(10)"}, /* 2A O SEND MESSAGE(10) */ {0x2A, C, "SEND MESSAGE(10)"}, /* 2B O OM O SEEK(10) */ {0x2B, D|W|R|O, "SEEK(10)"}, /* 2B O LOCATE */ {0x2B, T, "LOCATE"}, /* 2B O POSITION TO ELEMENT */ {0x2B, M, "POSITION TO ELEMENT"}, /* 2C V O ERASE(10) */ {0x2C, O, "ERASE(10)"}, /* 2D V O O READ UPDATED BLOCK */ {0x2D, W|O, "READ UPDATED BLOCK"}, /* 2E O O O WRITE AND VERIFY(10) */ {0x2E, D|W|O, "WRITE AND VERIFY(10)"}, /* 2F O OO O VERIFY(10) */ {0x2F, D|W|R|O, "VERIFY(10)"}, /* 30 Z ZO Z SEARCH DATA HIGH(10) */ {0x30, D|W|R|O, "SEARCH DATA HIGH(10)"}, /* 31 Z ZO Z SEARCH DATA EQUAL(10) */ {0x31, D|W|R|O, "SEARCH DATA EQUAL(10)"}, /* 31 O OBJECT POSITION */ {0x31, S, "OBJECT POSITION"}, /* 32 Z ZO Z SEARCH DATA LOW(10) */ {0x32, D|W|R|O, "SEARCH DATA LOW(10"}, /* 33 O OO O SET LIMITS(10) */ {0x33, D|W|R|O, "SET LIMITS(10)"}, /* 34 O OO O PRE-FETCH */ {0x34, D|W|R|O, "PRE-FETCH"}, /* 34 O READ POSITION */ {0x34, T, "READ POSITION"}, /* 34 O GET DATA BUFFER STATUS */ {0x34, S, "GET DATA BUFFER STATUS"}, /* 35 O OM O SYNCHRONIZE CACHE */ {0x35, D|W|R|O, "SYNCHRONIZE CACHE"}, /* 36 O OO O LOCK UNLOCK CACHE */ {0x36, D|W|R|O, "LOCK UNLOCK CACHE"}, /* 37 O O READ DEFECT DATA(10) */ {0x37, D|O, "READ DEFECT DATA(10)"}, /* 38 O O MEDIUM SCAN */ {0x38, W|O, "MEDIUM SCAN"}, /* 39 OOOOOOOO COMPARE */ {0x39, ALL & ~(M|C|A|E), "COMPARE"}, /* 3A OOOOOOOO COPY AND VERIFY */ {0x3A, ALL & ~(M|C|A|E), "COPY AND VERIFY"}, /* 3B OOOOOOOOOO O WRITE BUFFER */ {0x3B, ALL & ~(A), "WRITE BUFFER"}, /* 3C OOOOOOOOOO READ BUFFER */ {0x3C, ALL & ~(A|E),"READ BUFFER"}, /* 3D O O UPDATE BLOCK */ {0x3D, W|O, "UPDATE BLOCK"}, /* 3E O OO O READ LONG */ {0x3E, D|W|R|O, "READ LONG"}, /* 3F O O O WRITE LONG */ {0x3F, D|W|O, "WRITE LONG"}, /* 40 OOOOOOOOOO CHANGE DEFINITION */ {0x40, ALL & ~(A|E),"CHANGE DEFINITION"}, /* 41 O WRITE SAME */ {0x41, D, "WRITE SAME"}, /* 42 M READ SUB-CHANNEL */ {0x42, R, "READ SUB-CHANNEL"}, /* 43 M READ TOC/PMA/ATIP {MMC Proposed} */ {0x43, R, "READ TOC/PMA/ATIP {MMC Proposed}"}, /* 44 M REPORT DENSITY SUPPORT */ {0x44, T, "REPORT DENSITY SUPPORT"}, /* 44 M READ HEADER */ {0x44, R, "READ HEADER"}, /* 45 O PLAY AUDIO(10) */ {0x45, R, "PLAY AUDIO(10)"}, /* 46 */ /* 47 O PLAY AUDIO MSF */ {0x47, R, "PLAY AUDIO MSF"}, /* 48 O PLAY AUDIO TRACK INDEX */ {0x48, R, "PLAY AUDIO TRACK INDEX"}, /* 49 O PLAY TRACK RELATIVE(10) */ {0x49, R, "PLAY TRACK RELATIVE(10)"}, /* 4A */ /* 4B O PAUSE/RESUME */ {0x4B, R, "PAUSE/RESUME"}, /* 4C OOOOOOOOOOO LOG SELECT */ {0x4C, ALL & ~(E), "LOG SELECT"}, /* 4D OOOOOOOOOOO LOG SENSE */ {0x4D, ALL & ~(E), "LOG SENSE"}, /* 4E O STOP PLAY/SCAN {MMC Proposed} */ {0x4E, R, "STOP PLAY/SCAN {MMC Proposed}"}, /* 4F */ /* 50 O XDWRITE(10) */ {0x50, D, "XDWRITE(10)"}, /* 51 O XPWRITE(10) */ {0x51, D, "XPWRITE(10)"}, /* 51 M READ DISC INFORMATION {MMC Proposed} */ {0x51, R, "READ DISC INFORMATION {MMC Proposed}"}, /* 52 O XDREAD(10) */ {0x52, D, "XDREAD(10)"}, /* 52 M READ TRACK INFORMATION {MMC Proposed} */ {0x52, R, "READ TRACK INFORMATION {MMC Proposed}"}, /* 53 M RESERVE TRACK {MMC Proposed} */ {0x53, R, "RESERVE TRACK {MMC Proposed}"}, /* 54 O SEND OPC INFORMATION {MMC Proposed} */ {0x54, R, "SEND OPC INFORMATION {MMC Proposed}"}, /* 55 OOO OOOOOOOO MODE SELECT(10) */ {0x55, ALL & ~(P), "MODE SELECT(10)"}, /* 56 MMMOMMMM O RESERVE(10) */ {0x56, ALL & ~(M|C|A), "RESERVE(10)"}, /* 56 M RESERVE ELEMENT(10) */ {0x56, M, "RESERVE ELEMENT(10)"}, /* 57 MMMOMMMM O RELEASE(10) */ {0x57, ALL & ~(M|C|A), "RELEASE(10"}, /* 57 M RELEASE ELEMENT(10) */ {0x57, M, "RELEASE ELEMENT(10)"}, /* 58 O REPAIR TRACK {MMC Proposed} */ {0x58, R, "REPAIR TRACK {MMC Proposed}"}, /* 59 O READ MASTER CUE {MMC Proposed} */ {0x59, R, "READ MASTER CUE {MMC Proposed}"}, /* 5A OOO OOOOOOOO MODE SENSE(10) */ {0x5A, ALL & ~(P), "MODE SENSE(10)"}, /* 5B M CLOSE TRACK/SESSION {MMC Proposed} */ {0x5B, R, "CLOSE TRACK/SESSION {MMC Proposed}"}, /* 5C O READ BUFFER CAPACITY {MMC Proposed} */ {0x5C, R, "READ BUFFER CAPACITY {MMC Proposed}"}, /* 5D O SEND CUE SHEET {MMC Proposed} */ {0x5D, R, "SEND CUE SHEET {MMC Proposed}"}, /* 5E OOOOOOOOO O PERSISTENT RESERVE IN */ {0x5E, ALL & ~(C|A),"PERSISTENT RESERVE IN"}, /* 5F OOOOOOOOO O PERSISTENT RESERVE OUT */ {0x5F, ALL & ~(C|A),"PERSISTENT RESERVE OUT"}, /* 80 O XDWRITE EXTENDED(16) */ {0x80, D, "XDWRITE EXTENDED(16)"}, /* 81 O REBUILD(16) */ {0x81, D, "REBUILD(16)"}, /* 82 O REGENERATE(16) */ {0x82, D, "REGENERATE(16)"}, /* 83 */ /* 84 */ /* 85 */ /* 86 */ /* 87 */ /* 88 */ /* 89 */ /* 8A */ /* 8B */ /* 8C */ /* 8D */ /* 8E */ /* 8F */ /* 90 */ /* 91 */ /* 92 */ /* 93 */ /* 94 */ /* 95 */ /* 96 */ /* 97 */ /* 98 */ /* 99 */ /* 9A */ /* 9B */ /* 9C */ /* 9D */ /* 9E */ /* 9F */ /* A0 OOOOOOOOOOO REPORT LUNS */ {0xA0, ALL & ~(E), "REPORT LUNS"}, /* A1 O BLANK {MMC Proposed} */ {0xA1, R, "BLANK {MMC Proposed}"}, /* A2 O WRITE CD MSF {MMC Proposed} */ {0xA2, R, "WRITE CD MSF {MMC Proposed}"}, /* A3 M MAINTENANCE (IN) */ {0xA3, A, "MAINTENANCE (IN)"}, /* A4 O MAINTENANCE (OUT) */ {0xA4, A, "MAINTENANCE (OUT)"}, /* A5 O M MOVE MEDIUM */ {0xA5, T|M, "MOVE MEDIUM"}, /* A5 O PLAY AUDIO(12) */ {0xA5, R, "PLAY AUDIO(12)"}, /* A6 O EXCHANGE MEDIUM */ {0xA6, M, "EXCHANGE MEDIUM"}, /* A6 O LOAD/UNLOAD CD {MMC Proposed} */ {0xA6, R, "LOAD/UNLOAD CD {MMC Proposed}"}, /* A7 OO OO OO MOVE MEDIUM ATTACHED */ {0xA7, D|T|W|R|O|M, "MOVE MEDIUM ATTACHED"}, /* A8 OM O READ(12) */ {0xA8, W|R|O, "READ(12)"}, /* A8 O GET MESSAGE(12) */ {0xA8, C, "GET MESSAGE(12)"}, /* A9 O PLAY TRACK RELATIVE(12) */ {0xA9, R, "PLAY TRACK RELATIVE(12)"}, /* AA O O WRITE(12) */ {0xAA, W|O, "WRITE(12)"}, /* AA O WRITE CD(12) {MMC Proposed} */ {0xAA, R, "WRITE CD(12) {MMC Proposed}"}, /* AA O SEND MESSAGE(12) */ {0xAA, C, "SEND MESSAGE(12)"}, /* AB */ /* AC O ERASE(12) */ {0xAC, O, "ERASE(12)"}, /* AD */ /* AE O O WRITE AND VERIFY(12) */ {0xAE, W|O, "WRITE AND VERIFY(12)"}, /* AF OO O VERIFY(12) */ {0xAF, W|R|O, "VERIFY(12)"}, /* B0 ZO Z SEARCH DATA HIGH(12) */ {0xB0, W|R|O, "SEARCH DATA HIGH(12)"}, /* B1 ZO Z SEARCH DATA EQUAL(12) */ {0xB1, W|R|O, "SEARCH DATA EQUAL(12)"}, /* B2 ZO Z SEARCH DATA LOW(12) */ {0xB2, W|R|O, "SEARCH DATA LOW(12)"}, /* B3 OO O SET LIMITS(12) */ {0xB3, W|R|O, "SET LIMITS(12)"}, /* B4 OO OO OO READ ELEMENT STATUS ATTACHED */ {0xB4, D|T|W|R|O|M, "READ ELEMENT STATUS ATTACHED"}, /* B5 O REQUEST VOLUME ELEMENT ADDRESS */ {0xB5, M, "REQUEST VOLUME ELEMENT ADDRESS"}, /* B6 O SEND VOLUME TAG */ {0xB6, M, "SEND VOLUME TAG"}, /* B7 O READ DEFECT DATA(12) */ {0xB7, O, "READ DEFECT DATA(12)"}, /* B8 O M READ ELEMENT STATUS */ {0xB8, T|M, "READ ELEMENT STATUS"}, /* B8 O SET CD SPEED {MMC Proposed} */ {0xB8, R, "SET CD SPEED {MMC Proposed}"}, /* B9 M READ CD MSF {MMC Proposed} */ {0xB9, R, "READ CD MSF {MMC Proposed}"}, /* BA O SCAN {MMC Proposed} */ {0xBA, R, "SCAN {MMC Proposed}"}, /* BA M REDUNDANCY GROUP (IN) */ {0xBA, A, "REDUNDANCY GROUP (IN)"}, /* BB O SET CD-ROM SPEED {proposed} */ {0xBB, R, "SET CD-ROM SPEED {proposed}"}, /* BB O REDUNDANCY GROUP (OUT) */ {0xBB, A, "REDUNDANCY GROUP (OUT)"}, /* BC O PLAY CD {MMC Proposed} */ {0xBC, R, "PLAY CD {MMC Proposed}"}, /* BC M SPARE (IN) */ {0xBC, A, "SPARE (IN)"}, /* BD M MECHANISM STATUS {MMC Proposed} */ {0xBD, R, "MECHANISM STATUS {MMC Proposed}"}, /* BD O SPARE (OUT) */ {0xBD, A, "SPARE (OUT)"}, /* BE O READ CD {MMC Proposed} */ {0xBE, R, "READ CD {MMC Proposed}"}, /* BE M VOLUME SET (IN) */ {0xBE, A, "VOLUME SET (IN)"}, /* BF O VOLUME SET (OUT) */ {0xBF, A, "VOLUME SET (OUT)"} }; const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { caddr_t match; int i, j; u_int16_t opmask; u_int16_t pd_type; int num_ops[2]; struct op_table_entry *table[2]; int num_tables; pd_type = SID_TYPE(inq_data); match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)scsi_op_quirk_table, sizeof(scsi_op_quirk_table)/ sizeof(*scsi_op_quirk_table), sizeof(*scsi_op_quirk_table), scsi_inquiry_match); if (match != NULL) { table[0] = ((struct scsi_op_quirk_entry *)match)->op_table; num_ops[0] = ((struct scsi_op_quirk_entry *)match)->num_ops; table[1] = scsi_op_codes; num_ops[1] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]); num_tables = 2; } else { /* * If this is true, we have a vendor specific opcode that * wasn't covered in the quirk table. */ if ((opcode > 0xBF) || ((opcode > 0x5F) && (opcode < 0x80))) return("Vendor Specific Command"); table[0] = scsi_op_codes; num_ops[0] = sizeof(scsi_op_codes)/sizeof(scsi_op_codes[0]); num_tables = 1; } opmask = 1 << pd_type; for (j = 0; j < num_tables; j++) { for (i = 0;i < num_ops[j] && table[j][i].opcode <= opcode; i++){ if ((table[j][i].opcode == opcode) && ((table[j][i].opmask & opmask) != 0)) return(table[j][i].desc); } } /* * If we can't find a match for the command in the table, we just * assume it's a vendor specifc command. */ return("Vendor Specific Command"); } #else /* SCSI_NO_OP_STRINGS */ const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data) { return(""); } #endif #include #if !defined(SCSI_NO_SENSE_STRINGS) #define SST(asc, ascq, action, desc) \ asc, ascq, action, desc #else #define SST(asc, ascq, action, desc) \ asc, asc, action #endif /* * If we're in the kernel, 'quantum' is already defined in cam_xpt.c. * Otherwise, we need to define it. */ #ifdef _KERNEL extern const char quantum[]; #else static const char quantum[] = "QUANTUM"; #endif /* * WARNING: You must update the num_ascs field below for this quirk table * entry if you add more entries. */ static struct asc_table_entry quantum_fireball_entries[] = { {SST(0x04, 0x0b, SS_START|SSQ_DECREMENT_COUNT|ENXIO, "Logical unit not ready, initializing cmd. required")} }; static struct scsi_sense_quirk_entry asc_quirk_table[] = { { /* * The Quantum Fireball ST and SE like to return 0x04 0x0b when * they really should return 0x04 0x02. 0x04,0x0b isn't * defined in any SCSI spec, and it isn't mentioned in the * hardware manual for these drives. */ {T_DIRECT, SIP_MEDIA_FIXED, "QUANTUM", "FIREBALL S*", "*"}, 1, /* number of vendor-specific sense codes for this entry */ quantum_fireball_entries } }; static struct asc_table_entry asc_text[] = { /* * From File: ASC-NUM.TXT * SCSI ASC/ASCQ Assignments * Numeric Sorted Listing * as of 5/12/97 * * D - DIRECT ACCESS DEVICE (SBC) device column key * .T - SEQUENTIAL ACCESS DEVICE (SSC) ------------------- * . L - PRINTER DEVICE (SSC) blank = reserved * . P - PROCESSOR DEVICE (SPC) not blank = allowed * . .W - WRITE ONCE READ MULTIPLE DEVICE (SBC) * . . R - CD DEVICE (MMC) * . . S - SCANNER DEVICE (SGC) * . . .O - OPTICAL MEMORY DEVICE (SBC) * . . . M - MEDIA CHANGER DEVICE (SMC) * . . . C - COMMUNICATION DEVICE (SSC) * . . . .A - STORAGE ARRAY DEVICE (SCC) * . . . . E - ENCLOSURE SERVICES DEVICE (SES) * DTLPWRSOMCAE ASC ASCQ Action Description * ------------ ---- ---- ------ -----------------------------------*/ /* DTLPWRSOMCAE */{SST(0x00, 0x00, SS_NEPDEF, "No additional sense information") }, /* T S */{SST(0x00, 0x01, SS_DEF, "Filemark detected") }, /* T S */{SST(0x00, 0x02, SS_DEF, "End-of-partition/medium detected") }, /* T */{SST(0x00, 0x03, SS_DEF, "Setmark detected") }, /* T S */{SST(0x00, 0x04, SS_DEF, "Beginning-of-partition/medium detected") }, /* T S */{SST(0x00, 0x05, SS_DEF, "End-of-data detected") }, /* DTLPWRSOMCAE */{SST(0x00, 0x06, SS_DEF, "I/O process terminated") }, /* R */{SST(0x00, 0x11, SS_NEDEF|EBUSY, "Audio play operation in progress") }, /* R */{SST(0x00, 0x12, SS_NEDEF, "Audio play operation paused") }, /* R */{SST(0x00, 0x13, SS_NEDEF, "Audio play operation successfully completed") }, /* R */{SST(0x00, 0x14, SS_DEF, "Audio play operation stopped due to error") }, /* R */{SST(0x00, 0x15, SS_DEF, "No current audio status to return") }, /* DTLPWRSOMCAE */{SST(0x00, 0x16, SS_NEDEF|EBUSY, "Operation in progress") }, /* DTL WRSOM AE */{SST(0x00, 0x17, SS_DEF, "Cleaning requested") }, /* D W O */{SST(0x01, 0x00, SS_DEF, "No index/sector signal") }, /* D WR OM */{SST(0x02, 0x00, SS_DEF, "No seek complete") }, /* DTL W SO */{SST(0x03, 0x00, SS_DEF, "Peripheral device write fault") }, /* T */{SST(0x03, 0x01, SS_DEF, "No write current") }, /* T */{SST(0x03, 0x02, SS_DEF, "Excessive write errors") }, /* DTLPWRSOMCAE */{SST(0x04, 0x00, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EIO, "Logical unit not ready, cause not reportable") }, /* DTLPWRSOMCAE */{SST(0x04, 0x01, SS_TUR|SSQ_MANY|SSQ_DECREMENT_COUNT|EBUSY, "Logical unit is in process of becoming ready") }, /* DTLPWRSOMCAE */{SST(0x04, 0x02, SS_START|SSQ_DECREMENT_COUNT|ENXIO, "Logical unit not ready, initializing cmd. required") }, /* DTLPWRSOMCAE */{SST(0x04, 0x03, SS_NEDEF|ENXIO, "Logical unit not ready, manual intervention required")}, /* DTL O */{SST(0x04, 0x04, SS_NEDEF|EBUSY, "Logical unit not ready, format in progress") }, /* DT W OMCA */{SST(0x04, 0x05, SS_NEDEF|EBUSY, "Logical unit not ready, rebuild in progress") }, /* DT W OMCA */{SST(0x04, 0x06, SS_NEDEF|EBUSY, "Logical unit not ready, recalculation in progress") }, /* DTLPWRSOMCAE */{SST(0x04, 0x07, SS_NEDEF|EBUSY, "Logical unit not ready, operation in progress") }, /* R */{SST(0x04, 0x08, SS_NEDEF|EBUSY, "Logical unit not ready, long write in progress") }, /* DTL WRSOMCAE */{SST(0x05, 0x00, SS_DEF, "Logical unit does not respond to selection") }, /* D WR OM */{SST(0x06, 0x00, SS_DEF, "No reference position found") }, /* DTL WRSOM */{SST(0x07, 0x00, SS_DEF, "Multiple peripheral devices selected") }, /* DTL WRSOMCAE */{SST(0x08, 0x00, SS_DEF, "Logical unit communication failure") }, /* DTL WRSOMCAE */{SST(0x08, 0x01, SS_DEF, "Logical unit communication time-out") }, /* DTL WRSOMCAE */{SST(0x08, 0x02, SS_DEF, "Logical unit communication parity error") }, /* DT R OM */{SST(0x08, 0x03, SS_DEF, "Logical unit communication crc error (ultra-dma/32)")}, /* DT WR O */{SST(0x09, 0x00, SS_DEF, "Track following error") }, /* WR O */{SST(0x09, 0x01, SS_DEF, "Tracking servo failure") }, /* WR O */{SST(0x09, 0x02, SS_DEF, "Focus servo failure") }, /* WR O */{SST(0x09, 0x03, SS_DEF, "Spindle servo failure") }, /* DT WR O */{SST(0x09, 0x04, SS_DEF, "Head select fault") }, /* DTLPWRSOMCAE */{SST(0x0A, 0x00, SS_NEDEF|ENOSPC, "Error log overflow") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x00, SS_DEF, "Warning") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x01, SS_DEF, "Specified temperature exceeded") }, /* DTLPWRSOMCAE */{SST(0x0B, 0x02, SS_DEF, "Enclosure degraded") }, /* T RS */{SST(0x0C, 0x00, SS_DEF, "Write error") }, /* D W O */{SST(0x0C, 0x01, SS_NEDEF, "Write error - recovered with auto reallocation") }, /* D W O */{SST(0x0C, 0x02, SS_DEF, "Write error - auto reallocation failed") }, /* D W O */{SST(0x0C, 0x03, SS_DEF, "Write error - recommend reassignment") }, /* DT W O */{SST(0x0C, 0x04, SS_NEPDEF, "Compression check miscompare error") }, /* DT W O */{SST(0x0C, 0x05, SS_DEF, "Data expansion occurred during compression") }, /* DT W O */{SST(0x0C, 0x06, SS_DEF, "Block not compressible") }, /* R */{SST(0x0C, 0x07, SS_DEF, "Write error - recovery needed") }, /* R */{SST(0x0C, 0x08, SS_DEF, "Write error - recovery failed") }, /* R */{SST(0x0C, 0x09, SS_DEF, "Write error - loss of streaming") }, /* R */{SST(0x0C, 0x0A, SS_DEF, "Write error - padding blocks added") }, /* D W O */{SST(0x10, 0x00, SS_DEF, "ID CRC or ECC error") }, /* DT WRSO */{SST(0x11, 0x00, SS_DEF, "Unrecovered read error") }, /* DT W SO */{SST(0x11, 0x01, SS_DEF, "Read retries exhausted") }, /* DT W SO */{SST(0x11, 0x02, SS_DEF, "Error too long to correct") }, /* DT W SO */{SST(0x11, 0x03, SS_DEF, "Multiple read errors") }, /* D W O */{SST(0x11, 0x04, SS_DEF, "Unrecovered read error - auto reallocate failed") }, /* WR O */{SST(0x11, 0x05, SS_DEF, "L-EC uncorrectable error") }, /* WR O */{SST(0x11, 0x06, SS_DEF, "CIRC unrecovered error") }, /* W O */{SST(0x11, 0x07, SS_DEF, "Data re-synchronization error") }, /* T */{SST(0x11, 0x08, SS_DEF, "Incomplete block read") }, /* T */{SST(0x11, 0x09, SS_DEF, "No gap found") }, /* DT O */{SST(0x11, 0x0A, SS_DEF, "Miscorrected error") }, /* D W O */{SST(0x11, 0x0B, SS_DEF, "Unrecovered read error - recommend reassignment") }, /* D W O */{SST(0x11, 0x0C, SS_DEF, "Unrecovered read error - recommend rewrite the data")}, /* DT WR O */{SST(0x11, 0x0D, SS_DEF, "De-compression CRC error") }, /* DT WR O */{SST(0x11, 0x0E, SS_DEF, "Cannot decompress using declared algorithm") }, /* R */{SST(0x11, 0x0F, SS_DEF, "Error reading UPC/EAN number") }, /* R */{SST(0x11, 0x10, SS_DEF, "Error reading ISRC number") }, /* R */{SST(0x11, 0x11, SS_DEF, "Read error - loss of streaming") }, /* D W O */{SST(0x12, 0x00, SS_DEF, "Address mark not found for id field") }, /* D W O */{SST(0x13, 0x00, SS_DEF, "Address mark not found for data field") }, /* DTL WRSO */{SST(0x14, 0x00, SS_DEF, "Recorded entity not found") }, /* DT WR O */{SST(0x14, 0x01, SS_DEF, "Record not found") }, /* T */{SST(0x14, 0x02, SS_DEF, "Filemark or setmark not found") }, /* T */{SST(0x14, 0x03, SS_DEF, "End-of-data not found") }, /* T */{SST(0x14, 0x04, SS_DEF, "Block sequence error") }, /* DT W O */{SST(0x14, 0x05, SS_DEF, "Record not found - recommend reassignment") }, /* DT W O */{SST(0x14, 0x06, SS_DEF, "Record not found - data auto-reallocated") }, /* DTL WRSOM */{SST(0x15, 0x00, SS_DEF, "Random positioning error") }, /* DTL WRSOM */{SST(0x15, 0x01, SS_DEF, "Mechanical positioning error") }, /* DT WR O */{SST(0x15, 0x02, SS_DEF, "Positioning error detected by read of medium") }, /* D W O */{SST(0x16, 0x00, SS_DEF, "Data synchronization mark error") }, /* D W O */{SST(0x16, 0x01, SS_DEF, "Data sync error - data rewritten") }, /* D W O */{SST(0x16, 0x02, SS_DEF, "Data sync error - recommend rewrite") }, /* D W O */{SST(0x16, 0x03, SS_NEDEF, "Data sync error - data auto-reallocated") }, /* D W O */{SST(0x16, 0x04, SS_DEF, "Data sync error - recommend reassignment") }, /* DT WRSO */{SST(0x17, 0x00, SS_NEDEF, "Recovered data with no error correction applied") }, /* DT WRSO */{SST(0x17, 0x01, SS_NEDEF, "Recovered data with retries") }, /* DT WR O */{SST(0x17, 0x02, SS_NEDEF, "Recovered data with positive head offset") }, /* DT WR O */{SST(0x17, 0x03, SS_NEDEF, "Recovered data with negative head offset") }, /* WR O */{SST(0x17, 0x04, SS_NEDEF, "Recovered data with retries and/or CIRC applied") }, /* D WR O */{SST(0x17, 0x05, SS_NEDEF, "Recovered data using previous sector id") }, /* D W O */{SST(0x17, 0x06, SS_NEDEF, "Recovered data without ECC - data auto-reallocated") }, /* D W O */{SST(0x17, 0x07, SS_NEDEF, "Recovered data without ECC - recommend reassignment")}, /* D W O */{SST(0x17, 0x08, SS_NEDEF, "Recovered data without ECC - recommend rewrite") }, /* D W O */{SST(0x17, 0x09, SS_NEDEF, "Recovered data without ECC - data rewritten") }, /* D W O */{SST(0x18, 0x00, SS_NEDEF, "Recovered data with error correction applied") }, /* D WR O */{SST(0x18, 0x01, SS_NEDEF, "Recovered data with error corr. & retries applied") }, /* D WR O */{SST(0x18, 0x02, SS_NEDEF, "Recovered data - data auto-reallocated") }, /* R */{SST(0x18, 0x03, SS_NEDEF, "Recovered data with CIRC") }, /* R */{SST(0x18, 0x04, SS_NEDEF, "Recovered data with L-EC") }, /* D WR O */{SST(0x18, 0x05, SS_NEDEF, "Recovered data - recommend reassignment") }, /* D WR O */{SST(0x18, 0x06, SS_NEDEF, "Recovered data - recommend rewrite") }, /* D W O */{SST(0x18, 0x07, SS_NEDEF, "Recovered data with ECC - data rewritten") }, /* D O */{SST(0x19, 0x00, SS_DEF, "Defect list error") }, /* D O */{SST(0x19, 0x01, SS_DEF, "Defect list not available") }, /* D O */{SST(0x19, 0x02, SS_DEF, "Defect list error in primary list") }, /* D O */{SST(0x19, 0x03, SS_DEF, "Defect list error in grown list") }, /* DTLPWRSOMCAE */{SST(0x1A, 0x00, SS_DEF, "Parameter list length error") }, /* DTLPWRSOMCAE */{SST(0x1B, 0x00, SS_DEF, "Synchronous data transfer error") }, /* D O */{SST(0x1C, 0x00, SS_DEF, "Defect list not found") }, /* D O */{SST(0x1C, 0x01, SS_DEF, "Primary defect list not found") }, /* D O */{SST(0x1C, 0x02, SS_DEF, "Grown defect list not found") }, /* D W O */{SST(0x1D, 0x00, SS_NEPDEF, "Miscompare during verify operation" )}, /* D W O */{SST(0x1E, 0x00, SS_NEDEF, "Recovered id with ecc correction") }, /* D O */{SST(0x1F, 0x00, SS_DEF, "Partial defect list transfer") }, /* DTLPWRSOMCAE */{SST(0x20, 0x00, SS_DEF, "Invalid command operation code") }, /* DT WR OM */{SST(0x21, 0x00, SS_DEF, "Logical block address out of range" )}, /* DT WR OM */{SST(0x21, 0x01, SS_DEF, "Invalid element address") }, /* D */{SST(0x22, 0x00, SS_DEF, "Illegal function") }, /* Deprecated. Use 20 00, 24 00, or 26 00 instead */ /* DTLPWRSOMCAE */{SST(0x24, 0x00, SS_NEDEF|EINVAL, "Invalid field in CDB") }, /* DTLPWRSOMCAE */{SST(0x25, 0x00, SS_NEDEF|ENXIO, "Logical unit not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x00, SS_NEDEF|EINVAL, "Invalid field in parameter list") }, /* DTLPWRSOMCAE */{SST(0x26, 0x01, SS_NEDEF|EINVAL, "Parameter not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x02, SS_NEDEF|EINVAL, "Parameter value invalid") }, /* DTLPWRSOMCAE */{SST(0x26, 0x03, SS_DEF, "Threshold parameters not supported") }, /* DTLPWRSOMCAE */{SST(0x26, 0x04, SS_DEF, "Invalid release of active persistent reservation") }, /* DT W O */{SST(0x27, 0x00, SS_NEDEF|EACCES, "Write protected") }, /* DT W O */{SST(0x27, 0x01, SS_NEDEF|EACCES, "Hardware write protected") }, /* DT W O */{SST(0x27, 0x02, SS_NEDEF|EACCES, "Logical unit software write protected") }, /* T */{SST(0x27, 0x03, SS_NEDEF|EACCES, "Associated write protect") }, /* T */{SST(0x27, 0x04, SS_NEDEF|EACCES, "Persistent write protect") }, /* T */{SST(0x27, 0x05, SS_NEDEF|EACCES, "Permanent write protect") }, /* DTLPWRSOMCAE */{SST(0x28, 0x00, SS_NEDEF|ENXIO, "Not ready to ready change, medium may have changed") }, /* DT WR OM */{SST(0x28, 0x01, SS_DEF, "Import or export element accessed") }, /* DTLPWRSOMCAE */{SST(0x29, 0x00, SS_NEDEF|ENXIO, "Power on, reset, or bus device reset occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x01, SS_DEF, "Power on occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x02, SS_DEF, "Scsi bus reset occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x03, SS_DEF, "Bus device reset function occurred") }, /* DTLPWRSOMCAE */{SST(0x29, 0x04, SS_DEF, "Device internal reset") }, /* DTLPWRSOMCAE */{SST(0x29, 0x05, SS_DEF, "Transceiver mode changed to single-ended") }, /* DTLPWRSOMCAE */{SST(0x29, 0x06, SS_DEF, "Transceiver mode changed to LVD") }, /* DTL WRSOMCAE */{SST(0x2A, 0x00, SS_DEF, "Parameters changed") }, /* DTL WRSOMCAE */{SST(0x2A, 0x01, SS_DEF, "Mode parameters changed") }, /* DTL WRSOMCAE */{SST(0x2A, 0x02, SS_DEF, "Log parameters changed") }, /* DTLPWRSOMCAE */{SST(0x2A, 0x03, SS_DEF, "Reservations preempted") }, /* DTLPWRSO C */{SST(0x2B, 0x00, SS_DEF, "Copy cannot execute since host cannot disconnect") }, /* DTLPWRSOMCAE */{SST(0x2C, 0x00, SS_DEF, "Command sequence error") }, /* S */{SST(0x2C, 0x01, SS_DEF, "Too many windows specified") }, /* S */{SST(0x2C, 0x02, SS_DEF, "Invalid combination of windows specified") }, /* R */{SST(0x2C, 0x03, SS_DEF, "Current program area is not empty") }, /* R */{SST(0x2C, 0x04, SS_DEF, "Current program area is empty") }, /* T */{SST(0x2D, 0x00, SS_DEF, "Overwrite error on update in place") }, /* DTLPWRSOMCAE */{SST(0x2F, 0x00, SS_DEF, "Commands cleared by another initiator") }, /* DT WR OM */{SST(0x30, 0x00, SS_DEF, "Incompatible medium installed") }, /* DT WR O */{SST(0x30, 0x01, SS_DEF, "Cannot read medium - unknown format") }, /* DT WR O */{SST(0x30, 0x02, SS_DEF, "Cannot read medium - incompatible format") }, /* DT */{SST(0x30, 0x03, SS_DEF, "Cleaning cartridge installed") }, /* DT WR O */{SST(0x30, 0x04, SS_DEF, "Cannot write medium - unknown format") }, /* DT WR O */{SST(0x30, 0x05, SS_DEF, "Cannot write medium - incompatible format") }, /* DT W O */{SST(0x30, 0x06, SS_DEF, "Cannot format medium - incompatible medium") }, /* DTL WRSOM AE */{SST(0x30, 0x07, SS_DEF, "Cleaning failure") }, /* R */{SST(0x30, 0x08, SS_DEF, "Cannot write - application code mismatch") }, /* R */{SST(0x30, 0x09, SS_DEF, "Current session not fixated for append") }, /* DT WR O */{SST(0x31, 0x00, SS_DEF, "Medium format corrupted") }, /* D L R O */{SST(0x31, 0x01, SS_DEF, "Format command failed") }, /* D W O */{SST(0x32, 0x00, SS_DEF, "No defect spare location available") }, /* D W O */{SST(0x32, 0x01, SS_DEF, "Defect list update failure") }, /* T */{SST(0x33, 0x00, SS_DEF, "Tape length error") }, /* DTLPWRSOMCAE */{SST(0x34, 0x00, SS_DEF, "Enclosure failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x00, SS_DEF, "Enclosure services failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x01, SS_DEF, "Unsupported enclosure function") }, /* DTLPWRSOMCAE */{SST(0x35, 0x02, SS_DEF, "Enclosure services unavailable") }, /* DTLPWRSOMCAE */{SST(0x35, 0x03, SS_DEF, "Enclosure services transfer failure") }, /* DTLPWRSOMCAE */{SST(0x35, 0x04, SS_DEF, "Enclosure services transfer refused") }, /* L */{SST(0x36, 0x00, SS_DEF, "Ribbon, ink, or toner failure") }, /* DTL WRSOMCAE */{SST(0x37, 0x00, SS_DEF, "Rounded parameter") }, /* DTL WRSOMCAE */{SST(0x39, 0x00, SS_DEF, "Saving parameters not supported") }, /* DTL WRSOM */{SST(0x3A, 0x00, SS_NEDEF|ENXIO, "Medium not present") }, /* DT WR OM */{SST(0x3A, 0x01, SS_NEDEF|ENXIO, "Medium not present - tray closed") }, /* DT WR OM */{SST(0x3A, 0x02, SS_NEDEF|ENXIO, "Medium not present - tray open") }, /* TL */{SST(0x3B, 0x00, SS_DEF, "Sequential positioning error") }, /* T */{SST(0x3B, 0x01, SS_DEF, "Tape position error at beginning-of-medium") }, /* T */{SST(0x3B, 0x02, SS_DEF, "Tape position error at end-of-medium") }, /* L */{SST(0x3B, 0x03, SS_DEF, "Tape or electronic vertical forms unit not ready") }, /* L */{SST(0x3B, 0x04, SS_DEF, "Slew failure") }, /* L */{SST(0x3B, 0x05, SS_DEF, "Paper jam") }, /* L */{SST(0x3B, 0x06, SS_DEF, "Failed to sense top-of-form") }, /* L */{SST(0x3B, 0x07, SS_DEF, "Failed to sense bottom-of-form") }, /* T */{SST(0x3B, 0x08, SS_DEF, "Reposition error") }, /* S */{SST(0x3B, 0x09, SS_DEF, "Read past end of medium") }, /* S */{SST(0x3B, 0x0A, SS_DEF, "Read past beginning of medium") }, /* S */{SST(0x3B, 0x0B, SS_DEF, "Position past end of medium") }, /* T S */{SST(0x3B, 0x0C, SS_DEF, "Position past beginning of medium") }, /* DT WR OM */{SST(0x3B, 0x0D, SS_NEDEF|ENOSPC, "Medium destination element full") }, /* DT WR OM */{SST(0x3B, 0x0E, SS_DEF, "Medium source element empty") }, /* R */{SST(0x3B, 0x0F, SS_DEF, "End of medium reached") }, /* DT WR OM */{SST(0x3B, 0x11, SS_DEF, "Medium magazine not accessible") }, /* DT WR OM */{SST(0x3B, 0x12, SS_DEF, "Medium magazine removed") }, /* DT WR OM */{SST(0x3B, 0x13, SS_DEF, "Medium magazine inserted") }, /* DT WR OM */{SST(0x3B, 0x14, SS_DEF, "Medium magazine locked") }, /* DT WR OM */{SST(0x3B, 0x15, SS_DEF, "Medium magazine unlocked") }, /* DTLPWRSOMCAE */{SST(0x3D, 0x00, SS_DEF, "Invalid bits in identify message") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x00, SS_DEF, "Logical unit has not self-configured yet") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x01, SS_DEF, "Logical unit failure") }, /* DTLPWRSOMCAE */{SST(0x3E, 0x02, SS_DEF, "Timeout on logical unit") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x00, SS_DEF, "Target operating conditions have changed") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x01, SS_DEF, "Microcode has been changed") }, /* DTLPWRSOMC */{SST(0x3F, 0x02, SS_DEF, "Changed operating definition") }, /* DTLPWRSOMCAE */{SST(0x3F, 0x03, SS_DEF, "Inquiry data has changed") }, /* DT WR OMCAE */{SST(0x3F, 0x04, SS_DEF, "Component device attached") }, /* DT WR OMCAE */{SST(0x3F, 0x05, SS_DEF, "Device identifier changed") }, /* DT WR OMCAE */{SST(0x3F, 0x06, SS_DEF, "Redundancy group created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x07, SS_DEF, "Redundancy group deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x08, SS_DEF, "Spare created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x09, SS_DEF, "Spare deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x0A, SS_DEF, "Volume set created or modified") }, /* DT WR OMCAE */{SST(0x3F, 0x0B, SS_DEF, "Volume set deleted") }, /* DT WR OMCAE */{SST(0x3F, 0x0C, SS_DEF, "Volume set deassigned") }, /* DT WR OMCAE */{SST(0x3F, 0x0D, SS_DEF, "Volume set reassigned") }, /* D */{SST(0x40, 0x00, SS_DEF, "Ram failure") }, /* deprecated - use 40 NN instead */ /* DTLPWRSOMCAE */{SST(0x40, 0x80, SS_DEF, "Diagnostic failure: ASCQ = Component ID") }, /* DTLPWRSOMCAE */{SST(0x40, 0xFF, SS_DEF|SSQ_RANGE, NULL) },/* Range 0x80->0xFF */ /* D */{SST(0x41, 0x00, SS_DEF, "Data path failure") }, /* deprecated - use 40 NN instead */ /* D */{SST(0x42, 0x00, SS_DEF, "Power-on or self-test failure") }, /* deprecated - use 40 NN instead */ /* DTLPWRSOMCAE */{SST(0x43, 0x00, SS_DEF, "Message error") }, /* DTLPWRSOMCAE */{SST(0x44, 0x00, SS_DEF, "Internal target failure") }, /* DTLPWRSOMCAE */{SST(0x45, 0x00, SS_DEF, "Select or reselect failure") }, /* DTLPWRSOMC */{SST(0x46, 0x00, SS_DEF, "Unsuccessful soft reset") }, /* DTLPWRSOMCAE */{SST(0x47, 0x00, SS_DEF, "SCSI parity error") }, /* DTLPWRSOMCAE */{SST(0x48, 0x00, SS_DEF, "Initiator detected error message received") }, /* DTLPWRSOMCAE */{SST(0x49, 0x00, SS_DEF, "Invalid message error") }, /* DTLPWRSOMCAE */{SST(0x4A, 0x00, SS_DEF, "Command phase error") }, /* DTLPWRSOMCAE */{SST(0x4B, 0x00, SS_DEF, "Data phase error") }, /* DTLPWRSOMCAE */{SST(0x4C, 0x00, SS_DEF, "Logical unit failed self-configuration") }, /* DTLPWRSOMCAE */{SST(0x4D, 0x00, SS_DEF, "Tagged overlapped commands: ASCQ = Queue tag ID") }, /* DTLPWRSOMCAE */{SST(0x4D, 0xFF, SS_DEF|SSQ_RANGE, NULL)}, /* Range 0x00->0xFF */ /* DTLPWRSOMCAE */{SST(0x4E, 0x00, SS_DEF, "Overlapped commands attempted") }, /* T */{SST(0x50, 0x00, SS_DEF, "Write append error") }, /* T */{SST(0x50, 0x01, SS_DEF, "Write append position error") }, /* T */{SST(0x50, 0x02, SS_DEF, "Position error related to timing") }, /* T O */{SST(0x51, 0x00, SS_DEF, "Erase failure") }, /* T */{SST(0x52, 0x00, SS_DEF, "Cartridge fault") }, /* DTL WRSOM */{SST(0x53, 0x00, SS_DEF, "Media load or eject failed") }, /* T */{SST(0x53, 0x01, SS_DEF, "Unload tape failure") }, /* DT WR OM */{SST(0x53, 0x02, SS_DEF, "Medium removal prevented") }, /* P */{SST(0x54, 0x00, SS_DEF, "Scsi to host system interface failure") }, /* P */{SST(0x55, 0x00, SS_DEF, "System resource failure") }, /* D O */{SST(0x55, 0x01, SS_NEDEF|ENOSPC, "System buffer full") }, /* R */{SST(0x57, 0x00, SS_DEF, "Unable to recover table-of-contents") }, /* O */{SST(0x58, 0x00, SS_DEF, "Generation does not exist") }, /* O */{SST(0x59, 0x00, SS_DEF, "Updated block read") }, /* DTLPWRSOM */{SST(0x5A, 0x00, SS_DEF, "Operator request or state change input") }, /* DT WR OM */{SST(0x5A, 0x01, SS_DEF, "Operator medium removal request") }, /* DT W O */{SST(0x5A, 0x02, SS_DEF, "Operator selected write protect") }, /* DT W O */{SST(0x5A, 0x03, SS_DEF, "Operator selected write permit") }, /* DTLPWRSOM */{SST(0x5B, 0x00, SS_DEF, "Log exception") }, /* DTLPWRSOM */{SST(0x5B, 0x01, SS_DEF, "Threshold condition met") }, /* DTLPWRSOM */{SST(0x5B, 0x02, SS_DEF, "Log counter at maximum") }, /* DTLPWRSOM */{SST(0x5B, 0x03, SS_DEF, "Log list codes exhausted") }, /* D O */{SST(0x5C, 0x00, SS_DEF, "RPL status change") }, /* D O */{SST(0x5C, 0x01, SS_NEDEF, "Spindles synchronized") }, /* D O */{SST(0x5C, 0x02, SS_DEF, "Spindles not synchronized") }, /* DTLPWRSOMCAE */{SST(0x5D, 0x00, SS_DEF, "Failure prediction threshold exceeded") }, /* DTLPWRSOMCAE */{SST(0x5D, 0xFF, SS_DEF, "Failure prediction threshold exceeded (false)") }, /* DTLPWRSO CA */{SST(0x5E, 0x00, SS_DEF, "Low power condition on") }, /* DTLPWRSO CA */{SST(0x5E, 0x01, SS_DEF, "Idle condition activated by timer") }, /* DTLPWRSO CA */{SST(0x5E, 0x02, SS_DEF, "Standby condition activated by timer") }, /* DTLPWRSO CA */{SST(0x5E, 0x03, SS_DEF, "Idle condition activated by command") }, /* DTLPWRSO CA */{SST(0x5E, 0x04, SS_DEF, "Standby condition activated by command") }, /* S */{SST(0x60, 0x00, SS_DEF, "Lamp failure") }, /* S */{SST(0x61, 0x00, SS_DEF, "Video acquisition error") }, /* S */{SST(0x61, 0x01, SS_DEF, "Unable to acquire video") }, /* S */{SST(0x61, 0x02, SS_DEF, "Out of focus") }, /* S */{SST(0x62, 0x00, SS_DEF, "Scan head positioning error") }, /* R */{SST(0x63, 0x00, SS_DEF, "End of user area encountered on this track") }, /* R */{SST(0x63, 0x01, SS_NEDEF|ENOSPC, "Packet does not fit in available space") }, /* R */{SST(0x64, 0x00, SS_DEF, "Illegal mode for this track") }, /* R */{SST(0x64, 0x01, SS_DEF, "Invalid packet size") }, /* DTLPWRSOMCAE */{SST(0x65, 0x00, SS_DEF, "Voltage fault") }, /* S */{SST(0x66, 0x00, SS_DEF, "Automatic document feeder cover up") }, /* S */{SST(0x66, 0x01, SS_DEF, "Automatic document feeder lift up") }, /* S */{SST(0x66, 0x02, SS_DEF, "Document jam in automatic document feeder") }, /* S */{SST(0x66, 0x03, SS_DEF, "Document miss feed automatic in document feeder") }, /* A */{SST(0x67, 0x00, SS_DEF, "Configuration failure") }, /* A */{SST(0x67, 0x01, SS_DEF, "Configuration of incapable logical units failed") }, /* A */{SST(0x67, 0x02, SS_DEF, "Add logical unit failed") }, /* A */{SST(0x67, 0x03, SS_DEF, "Modification of logical unit failed") }, /* A */{SST(0x67, 0x04, SS_DEF, "Exchange of logical unit failed") }, /* A */{SST(0x67, 0x05, SS_DEF, "Remove of logical unit failed") }, /* A */{SST(0x67, 0x06, SS_DEF, "Attachment of logical unit failed") }, /* A */{SST(0x67, 0x07, SS_DEF, "Creation of logical unit failed") }, /* A */{SST(0x68, 0x00, SS_DEF, "Logical unit not configured") }, /* A */{SST(0x69, 0x00, SS_DEF, "Data loss on logical unit") }, /* A */{SST(0x69, 0x01, SS_DEF, "Multiple logical unit failures") }, /* A */{SST(0x69, 0x02, SS_DEF, "Parity/data mismatch") }, /* A */{SST(0x6A, 0x00, SS_DEF, "Informational, refer to log") }, /* A */{SST(0x6B, 0x00, SS_DEF, "State change has occurred") }, /* A */{SST(0x6B, 0x01, SS_DEF, "Redundancy level got better") }, /* A */{SST(0x6B, 0x02, SS_DEF, "Redundancy level got worse") }, /* A */{SST(0x6C, 0x00, SS_DEF, "Rebuild failure occurred") }, /* A */{SST(0x6D, 0x00, SS_DEF, "Recalculate failure occurred") }, /* A */{SST(0x6E, 0x00, SS_DEF, "Command to logical unit failed") }, /* T */{SST(0x70, 0x00, SS_DEF, "Decompression exception short: ASCQ = Algorithm ID") }, /* T */{SST(0x70, 0xFF, SS_DEF|SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* T */{SST(0x71, 0x00, SS_DEF, "Decompression exception long: ASCQ = Algorithm ID") }, /* T */{SST(0x71, 0xFF, SS_DEF|SSQ_RANGE, NULL) }, /* Range 0x00 -> 0xFF */ /* R */{SST(0x72, 0x00, SS_DEF, "Session fixation error") }, /* R */{SST(0x72, 0x01, SS_DEF, "Session fixation error writing lead-in") }, /* R */{SST(0x72, 0x02, SS_DEF, "Session fixation error writing lead-out") }, /* R */{SST(0x72, 0x03, SS_DEF, "Session fixation error - incomplete track in session") }, /* R */{SST(0x72, 0x04, SS_DEF, "Empty or partially written reserved track") }, /* R */{SST(0x73, 0x00, SS_DEF, "CD control error") }, /* R */{SST(0x73, 0x01, SS_DEF, "Power calibration area almost full") }, /* R */{SST(0x73, 0x02, SS_NEDEF|ENOSPC, "Power calibration area is full") }, /* R */{SST(0x73, 0x03, SS_DEF, "Power calibration area error") }, /* R */{SST(0x73, 0x04, SS_DEF, "Program memory area update failure") }, /* R */{SST(0x73, 0x05, SS_DEF, "program memory area is full") } }; #if !defined(SCSI_NO_SENSE_STRINGS) const char * scsi_sense_desc(int asc, int ascq, struct scsi_inquiry_data *inq_data) { int i, j; caddr_t match; struct asc_table_entry *table[2]; int table_size[2]; int num_tables; if (inq_data == NULL) return(NULL); match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)asc_quirk_table, sizeof(asc_quirk_table)/sizeof(*asc_quirk_table), sizeof(*asc_quirk_table), scsi_inquiry_match); if (match != NULL) { table[0] = ((struct scsi_sense_quirk_entry *)match)->asc_info; table_size[0] = ((struct scsi_sense_quirk_entry *)match)->num_ascs; table[1] = asc_text; table_size[1] = sizeof(asc_text)/sizeof(asc_text[0]); num_tables = 2; } else { table[0] = asc_text; table_size[0] = sizeof(asc_text)/sizeof(asc_text[0]); num_tables = 1; } for (j = 0; j < num_tables; j++) { for (i = 0; i < table_size[j]; i++) { if (table[j][i].asc == asc) { /* Check for ranges */ if ((table[j][i].action & SSQ_RANGE) != 0) { if (table[j][i].ascq >= ascq && table[j][i-1].ascq <= ascq) return table[j][i-1].desc; continue; } if (table[j][i].ascq == ascq) return table[j][i].desc; } } } if (asc >= 0x80 && asc <= 0xff) return "Vendor Specific ASC"; if (ascq >= 0x80 && ascq <= 0xff) return "Vendor Specific ASCQ"; return "Reserved ASC/ASCQ pair"; } #else /* SCSI_NO_SENSE_STRINGS */ const char * scsi_sense_desc(int asc, int ascq, struct scsi_inquiry_data *inq_data) { return (""); } #endif /* * Given a particular failed CCB and its device type information, return * the appropriate action from either the sense code quirk table or the * sense code table. */ scsi_sense_action scsi_error_action(int asc, int ascq, struct scsi_inquiry_data *inq_data) { caddr_t match; struct asc_table_entry *table[2]; int table_size[2]; int num_tables; int i, j; /* * If we don't have inquiry data, we can't match against any quirk * entries. */ if (inq_data != NULL) { match = cam_quirkmatch((caddr_t)inq_data, (caddr_t)asc_quirk_table, sizeof(asc_quirk_table) / sizeof(*asc_quirk_table), sizeof(*asc_quirk_table), scsi_inquiry_match); } else match = NULL; if (match != NULL) { table[0] = ((struct scsi_sense_quirk_entry *)match)->asc_info; table_size[0] = ((struct scsi_sense_quirk_entry *)match)->num_ascs; table[1] = asc_text; table_size[1] = sizeof(asc_text)/sizeof(asc_text[0]); num_tables = 2; } else { table[0] = asc_text; table_size[0] = sizeof(asc_text)/sizeof(asc_text[0]); num_tables = 1; } for (j = 0; j < num_tables; j++) { for (i = 0; i < table_size[j]; i++) { if (table[j][i].asc == asc) { /* Check for ranges */ if ((table[j][i].action & SSQ_RANGE) != 0){ if (table[j][i].ascq >= ascq && table[j][i-1].ascq <= ascq) return table[j][i].action; continue; } /* * Check to see if we have a match. If the * current ascq in the table is greater * than our ascq, and there aren't any more * tables to search, just return the * default action. */ if (table[j][i].ascq == ascq) return(table[j][i].action); else if ((j == (num_tables - 1)) && (table[j][i].ascq > ascq)) return(SS_DEF); } } } /* * If we get to this point, it's most likely a vendor specific * ASC and we don't have a quirk entry for it. Oh well, we just * tell the error handling code to take the default action. */ return(SS_DEF); } char * scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len) { u_int8_t cdb_len; int i; if (cdb_ptr == NULL) return(""); /* Silence warnings */ cdb_len = 0; /* * This is taken from the SCSI-3 draft spec. * (T10/1157D revision 0.3) * The top 3 bits of an opcode are the group code. The next 5 bits * are the command code. * Group 0: six byte commands * Group 1: ten byte commands * Group 2: ten byte commands * Group 3: reserved * Group 4: sixteen byte commands * Group 5: twelve byte commands * Group 6: vendor specific * Group 7: vendor specific */ switch((*cdb_ptr >> 5) & 0x7) { case 0: cdb_len = 6; break; case 1: case 2: cdb_len = 10; break; case 3: case 6: case 7: /* in this case, just print out the opcode */ cdb_len = 1; break; case 4: cdb_len = 16; break; case 5: cdb_len = 12; break; } *cdb_string = '\0'; for (i = 0; i < cdb_len; i++) snprintf(cdb_string + strlen(cdb_string), len - strlen(cdb_string), "%x ", cdb_ptr[i]); return(cdb_string); } /* * scsi_sense_print will decode the sense data into human * readable form. Sense handlers can use this to generate * a report. */ /* * Because scsi_sense_print() utilizes transport layer functions, it will * only work in the kernel. */ #ifdef _KERNEL void scsi_sense_print(struct ccb_scsiio *csio) { struct scsi_sense_data *sense; u_int32_t info; int error_code; int sense_key; int asc, ascq; struct ccb_getdev cgd; u_int8_t command_print; sense = &csio->sense_data; /* * If the CDB is a physical address, we can't deal with it.. */ if ((csio->ccb_h.flags & CAM_CDB_PHYS) != 0) command_print = 0; else command_print = 1; /* * Get the device information. */ xpt_setup_ccb(&cgd.ccb_h, csio->ccb_h.path, /*priority*/ 1); cgd.ccb_h.func_code = XPT_GDEV_TYPE; xpt_action((union ccb *)&cgd); /* * If the device is unconfigured, just pretend that it is a hard * drive. scsi_op_desc() needs this. */ if (cgd.ccb_h.status == CAM_DEV_NOT_THERE) cgd.inq_data.device = T_DIRECT; if (command_print != 0) { char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; xpt_print_path(csio->ccb_h.path); if ((csio->ccb_h.flags & CAM_CDB_POINTER) != 0) { printf("%s. CDB: %s\n", scsi_op_desc(csio->cdb_io.cdb_ptr[0], &cgd.inq_data), scsi_cdb_string(csio->cdb_io.cdb_ptr, cdb_str, sizeof(cdb_str))); } else { printf("%s. CDB: %s\n", scsi_op_desc(csio->cdb_io.cdb_bytes[0], &cgd.inq_data), scsi_cdb_string( csio->cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str))); } } /* * If the sense data is a physical pointer, forget it. */ if (csio->ccb_h.flags & CAM_SENSE_PTR) { if (csio->ccb_h.flags & CAM_SENSE_PHYS) return; else { /* * XXX KDM this is stupid, but casting the * structure doesn't work... */ bcopy(&csio->sense_data, sense, sizeof(struct scsi_sense_data *)); } } else { /* * If the physical sense flag is set, but the sense pointer * is not also set, we assume that the user is an idiot and * return. (Well, okay, it could be that somehow, the * entire csio is physical, but we would have probably core * dumped on one of the bogus pointer deferences above * already.) */ if (csio->ccb_h.flags & CAM_SENSE_PHYS) return; else sense = &csio->sense_data; } xpt_print_path(csio->ccb_h.path); error_code = sense->error_code & SSD_ERRCODE; sense_key = sense->flags & SSD_KEY; switch (error_code) { case SSD_DEFERRED_ERROR: printf("Deferred Error: "); /* FALLTHROUGH */ case SSD_CURRENT_ERROR: printf("%s", scsi_sense_key_text[sense_key]); info = scsi_4btoul(sense->info); if (sense->error_code & SSD_ERRCODE_VALID) { switch (sense_key) { case SSD_KEY_NOT_READY: case SSD_KEY_ILLEGAL_REQUEST: case SSD_KEY_UNIT_ATTENTION: case SSD_KEY_DATA_PROTECT: break; case SSD_KEY_BLANK_CHECK: printf(" req sz: %d (decimal)", info); break; default: if (info) { if (sense->flags & SSD_ILI) { printf(" ILI (length mismatch):" " %d", info); } else { printf(" info:%x", info); } } } } else if (info) printf(" info?:%x", info); if (sense->extra_len >= 4) { if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) { printf(" csi:%x,%x,%x,%x", sense->cmd_spec_info[0], sense->cmd_spec_info[1], sense->cmd_spec_info[2], sense->cmd_spec_info[3]); } } asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; if (asc || ascq) { const char *desc = scsi_sense_desc(asc, ascq, &cgd.inq_data); printf(" asc:%x,%x\n", asc, ascq); xpt_print_path(csio->ccb_h.path); printf("%s", desc); } if (sense->extra_len >= 7 && sense->fru) { printf(" field replaceable unit: %x", sense->fru); } if ((sense->extra_len >= 10) && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) { printf(" sks:%x,%x", sense->sense_key_spec[0], scsi_2btoul(&sense->sense_key_spec[1])); } break; default: printf("error code %d", sense->error_code & SSD_ERRCODE); if (sense->error_code & SSD_ERRCODE_VALID) { printf(" at block no. %d (decimal)", info = scsi_4btoul(sense->info)); } } printf("\n"); } #else /* !_KERNEL */ char * scsi_sense_string(struct cam_device *device, struct ccb_scsiio *csio, char *str, int str_len) { struct scsi_sense_data *sense; u_int32_t info; int error_code; int sense_key; int asc, ascq; u_int8_t command_print; char path_str[64]; char tmpstr[2048]; int tmpstrlen = 2048; int cur_len = 0, tmplen = 0, retlen; if ((device == NULL) || (csio == NULL) || (str == NULL)) return(NULL); if (str_len <= 0) return(NULL); /* * If the CDB is a physical address, we can't deal with it.. */ if ((csio->ccb_h.flags & CAM_CDB_PHYS) != 0) command_print = 0; else command_print = 1; cam_path_string(device, path_str, 64); str[0] = '\0'; sense = NULL; if (command_print != 0) { char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; retlen = snprintf(tmpstr, tmpstrlen, "%s", path_str); if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; if ((csio->ccb_h.flags & CAM_CDB_POINTER) != 0) { retlen = snprintf(tmpstr, tmpstrlen, "%s. CDB: %s\n", scsi_op_desc(csio->cdb_io.cdb_ptr[0], &device->inq_data), scsi_cdb_string(csio->cdb_io.cdb_ptr, cdb_str, sizeof(cdb_str))); } else { retlen = snprintf(tmpstr, tmpstrlen, "%s. CDB: %s\n", scsi_op_desc(csio->cdb_io.cdb_bytes[0], &device->inq_data), scsi_cdb_string( csio->cdb_io.cdb_bytes, cdb_str, sizeof(cdb_str))); } if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } /* * If the sense data is a physical pointer, forget it. */ if (csio->ccb_h.flags & CAM_SENSE_PTR) { if (csio->ccb_h.flags & CAM_SENSE_PHYS) return(NULL); else { /* * XXX KDM this is stupid, but casting the * structure doesn't work... */ bcopy(&csio->sense_data, sense, sizeof(struct scsi_sense_data *)); } } else { /* * If the physical sense flag is set, but the sense pointer * is not also set, we assume that the user is an idiot and * return. (Well, okay, it could be that somehow, the * entire csio is physical, but we would have probably core * dumped on one of the bogus pointer deferences above * already.) */ if (csio->ccb_h.flags & CAM_SENSE_PHYS) return(NULL); else sense = &csio->sense_data; } retlen = snprintf(tmpstr, tmpstrlen, "%s", path_str); if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; error_code = sense->error_code & SSD_ERRCODE; sense_key = sense->flags & SSD_KEY; switch (error_code) { case SSD_DEFERRED_ERROR: retlen = snprintf(tmpstr, tmpstrlen, "Deferred Error: "); if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; /* FALLTHROUGH */ case SSD_CURRENT_ERROR: retlen = snprintf(tmpstr, tmpstrlen, "%s", scsi_sense_key_text[sense_key]); if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; info = scsi_4btoul(sense->info); if (sense->error_code & SSD_ERRCODE_VALID) { switch (sense_key) { case SSD_KEY_NOT_READY: case SSD_KEY_ILLEGAL_REQUEST: case SSD_KEY_UNIT_ATTENTION: case SSD_KEY_DATA_PROTECT: break; case SSD_KEY_BLANK_CHECK: retlen = snprintf(tmpstr, tmpstrlen, " req sz: %d (decimal)", info); if ((tmplen = str_len - cur_len - 1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; break; default: if (info) { if (sense->flags & SSD_ILI) { retlen = snprintf (tmpstr, tmpstrlen, " ILI (length " "mismatch): %d", info); } else { retlen = snprintf(tmpstr, tmpstrlen, " info:%x", info); } if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } } } else if (info) { retlen = snprintf(tmpstr, tmpstrlen," info?:%x", info); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } if (sense->extra_len >= 4) { if (bcmp(sense->cmd_spec_info, "\0\0\0\0", 4)) { retlen = snprintf(tmpstr, tmpstrlen, " csi:%x,%x,%x,%x", sense->cmd_spec_info[0], sense->cmd_spec_info[1], sense->cmd_spec_info[2], sense->cmd_spec_info[3]); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } } asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; if (asc || ascq) { const char *desc = scsi_sense_desc(asc, ascq, &device->inq_data); retlen = snprintf(tmpstr, tmpstrlen, " asc:%x,%x\n%s%s", asc, ascq, path_str, desc); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } if (sense->extra_len >= 7 && sense->fru) { retlen = snprintf(tmpstr, tmpstrlen, " field replaceable unit: %x", sense->fru); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); str[str_len - 1] = '\0'; cur_len += retlen; } if ((sense->extra_len >= 10) && (sense->sense_key_spec[0] & SSD_SCS_VALID) != 0) { retlen = snprintf(tmpstr, tmpstrlen, " sks:%x,%x", sense->sense_key_spec[0], scsi_2btoul(&sense->sense_key_spec[1])); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); str[str_len - 1] = '\0'; cur_len += retlen; } break; default: retlen = snprintf(tmpstr, tmpstrlen, "error code %d", sense->error_code & SSD_ERRCODE); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; if (sense->error_code & SSD_ERRCODE_VALID) { retlen = snprintf(tmpstr, tmpstrlen, " at block no. %d (decimal)", info = scsi_4btoul(sense->info)); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; } } retlen = snprintf(tmpstr, tmpstrlen, "\n"); if ((tmplen = str_len - cur_len -1) < 0) goto sst_bailout; strncat(str, tmpstr, tmplen); cur_len += retlen; str[str_len - 1] = '\0'; sst_bailout: return(str); } void scsi_sense_print(struct cam_device *device, struct ccb_scsiio *csio, FILE *ofile) { char str[2048]; if ((device == NULL) || (csio == NULL) || (ofile == NULL)) return; fprintf(ofile, "%s", scsi_sense_string(device, csio, str, 2048)); } #endif /* _KERNEL/!_KERNEL */ #ifdef _KERNEL int scsi_interpret_sense(union ccb *ccb, u_int32_t sense_flags, u_int32_t *relsim_flags, u_int32_t *openings, u_int32_t *timeout, scsi_sense_action error_action) #else int scsi_interpret_sense(struct cam_device *device, union ccb *ccb, u_int32_t sense_flags, u_int32_t *relsim_flags, u_int32_t *openings, u_int32_t *timeout, scsi_sense_action error_action) #endif { struct scsi_sense_data *sense; int error_code, sense_key, asc, ascq; int error; int print_sense; struct ccb_scsiio *csio; int retry; csio = &ccb->csio; sense = &csio->sense_data; scsi_extract_sense(sense, &error_code, &sense_key, &asc, &ascq); #ifdef _KERNEL if (bootverbose) { sense_flags |= SF_PRINT_ALWAYS; print_sense = TRUE; } else if ((sense_flags & SF_NO_PRINT) == 0) #else if ((sense_flags & SF_NO_PRINT) == 0) #endif print_sense = TRUE; else print_sense = FALSE; switch (error_code) { case SSD_DEFERRED_ERROR: { /* * XXX dufault@FreeBSD.org * This error doesn't relate to the command associated * with this request sense. A deferred error is an error * for a command that has already returned GOOD status * (see 8.2.14.2). * * By my reading of that section, it looks like the current * command has been cancelled, we should now clean things up * (hopefully recovering any lost data) and then retry the * current command. There are two easy choices, both wrong: * * 1. Drop through (like we had been doing), thus treating * this as if the error were for the current command and * return and stop the current command. * * 2. Issue a retry (like I made it do) thus hopefully * recovering the current transfer, and ignoring the * fact that we've dropped a command. * * These should probably be handled in a device specific * sense handler or punted back up to a user mode daemon */ /* decrement the number of retries */ retry = ccb->ccb_h.retry_count > 0; if (retry) ccb->ccb_h.retry_count--; error = ERESTART; break; } case SSD_CURRENT_ERROR: { switch (sense_key) { case SSD_KEY_NO_SENSE: /* Why were we called then? Well don't bail now */ /* FALLTHROUGH */ case SSD_KEY_EQUAL: /* These should be filtered by the peripheral drivers */ /* FALLTHROUGH */ case SSD_KEY_MISCOMPARE: print_sense = FALSE; /* FALLTHROUGH */ case SSD_KEY_RECOVERED_ERROR: /* decrement the number of retries */ retry = ccb->ccb_h.retry_count > 0; if (retry) ccb->ccb_h.retry_count--; error = 0; break; case SSD_KEY_ILLEGAL_REQUEST: if (((sense_flags & SF_QUIET_IR) != 0) && ((sense_flags & SF_PRINT_ALWAYS) == 0)) print_sense = FALSE; error = EINVAL; break; case SSD_KEY_NOT_READY: case SSD_KEY_DATA_PROTECT: case SSD_KEY_VOLUME_OVERFLOW: case SSD_KEY_BLANK_CHECK: /* should be filtered out by peripheral drivers */ retry = ccb->ccb_h.retry_count > 0; if (retry) { ccb->ccb_h.retry_count--; error = ERESTART; print_sense = FALSE; } else { if (((error_action & SSQ_PRINT_SENSE) == 0) && ((sense_flags & SF_PRINT_ALWAYS) == 0)) print_sense = FALSE; error = error_action & SS_ERRMASK; } break; case SSD_KEY_UNIT_ATTENTION: /* * This should also be filtered out by * peripheral drivers since each has a different * concept of what it means to invalidate the media. */ if ((sense_flags & SF_RETRY_UA) != 0) { /* don't decrement retry count */ error = ERESTART; print_sense = FALSE; } else { /* decrement the number of retries */ retry = ccb->ccb_h.retry_count > 0; if (retry) { ccb->ccb_h.retry_count--; error = ERESTART; print_sense = FALSE; } else { if (((error_action & SSQ_PRINT_SENSE) == 0) && ((sense_flags & SF_PRINT_ALWAYS) == 0)) print_sense = FALSE; error = error_action & SS_ERRMASK; } } break; default: /* decrement the number of retries */ retry = ccb->ccb_h.retry_count > 0; if (retry) { ccb->ccb_h.retry_count--; error = ERESTART; print_sense = FALSE; } else { if (((error_action & SSQ_PRINT_SENSE) == 0) && ((sense_flags & SF_PRINT_ALWAYS) == 0)) print_sense = FALSE; error = error_action & SS_ERRMASK; } } break; } default: /* decrement the number of retries */ retry = ccb->ccb_h.retry_count > 0; if (retry) { ccb->ccb_h.retry_count--; error = ERESTART; print_sense = FALSE; } else error = EIO; break; } if (print_sense) { #ifdef _KERNEL scsi_sense_print(csio); #else scsi_sense_print(device, csio, stdout); #endif } return (error); } +/* + * This function currently requires at least 36 bytes, or + * SHORT_INQUIRY_LENGTH, worth of data to function properly. If this + * function needs more or less data in the future, another length should be + * defined in scsi_all.h to indicate the minimum amount of data necessary + * for this routine to function properly. + */ void scsi_print_inquiry(struct scsi_inquiry_data *inq_data) { u_int8_t type; char *dtype, *qtype; char vendor[16], product[48], revision[16], rstr[4]; type = SID_TYPE(inq_data); /* * Figure out basic device type and qualifier. */ if (SID_QUAL_IS_VENDOR_UNIQUE(inq_data)) { qtype = "(vendor-unique qualifier)"; } else { switch (SID_QUAL(inq_data)) { case SID_QUAL_LU_CONNECTED: qtype = ""; break; case SID_QUAL_LU_OFFLINE: qtype = "(offline)"; break; case SID_QUAL_RSVD: qtype = "(reserved qualifier)"; break; default: case SID_QUAL_BAD_LU: qtype = "(lun not supported)"; break; } } switch (type) { case T_DIRECT: dtype = "Direct Access"; break; case T_SEQUENTIAL: dtype = "Sequential Access"; break; case T_PRINTER: dtype = "Printer"; break; case T_PROCESSOR: dtype = "Processor"; break; case T_CDROM: dtype = "CD-ROM"; break; case T_WORM: dtype = "Worm"; break; case T_SCANNER: dtype = "Scanner"; break; case T_OPTICAL: dtype = "Optical"; break; case T_CHANGER: dtype = "Changer"; break; case T_COMM: dtype = "Communication"; break; case T_STORARRAY: dtype = "Storage Arrray"; break; case T_ENCLOSURE: dtype = "Enclosure Services"; break; case T_NODEVICE: dtype = "Uninstalled"; default: dtype = "unknown"; break; } cam_strvis(vendor, inq_data->vendor, sizeof(inq_data->vendor), sizeof(vendor)); cam_strvis(product, inq_data->product, sizeof(inq_data->product), sizeof(product)); cam_strvis(revision, inq_data->revision, sizeof(inq_data->revision), sizeof(revision)); if (SID_ANSI_REV(inq_data) == SCSI_REV_CCS) bcopy("CCS", rstr, 4); else snprintf(rstr, sizeof (rstr), "%d", SID_ANSI_REV(inq_data)); printf("<%s %s %s> %s %s SCSI-%s device %s\n", vendor, product, revision, SID_IS_REMOVABLE(inq_data) ? "Removable" : "Fixed", dtype, rstr, qtype); } /* * Table of syncrates that don't follow the "divisible by 4" * rule. This table will be expanded in future SCSI specs. * I believe that FAST-40 has already been defined... */ static struct { u_int period_factor; u_int period; /* in 10ths of ns */ } scsi_syncrates[] = { { 0x09, 125 }, /* FAST-80 */ { 0x0a, 250 }, { 0x0b, 303 }, { 0x0c, 500 } }; /* * Return the frequency in kHz corresponding to the given * sync period factor. */ u_int scsi_calc_syncsrate(u_int period_factor) { int i; int num_syncrates; num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period_factor == scsi_syncrates[i].period_factor) { /* Period in kHz */ return (10000000 / scsi_syncrates[i].period); } } /* * Wasn't in the table, so use the standard * 4 times conversion. */ return (10000000 / (period_factor * 4 * 10)); } /* * Return the SCSI sync parameter that corresponsd to * the passed in period in 10ths of ns. */ u_int scsi_calc_syncparam(u_int period) { int i; int num_syncrates; if (period == 0) return (~0); /* Async */ num_syncrates = sizeof(scsi_syncrates) / sizeof(scsi_syncrates[0]); /* See if the period is in the "exception" table */ for (i = 0; i < num_syncrates; i++) { if (period <= scsi_syncrates[i].period) { /* Period in kHz */ return (scsi_syncrates[i].period_factor); } } /* * Wasn't in the table, so use the standard * 1/4 period in ns conversion. */ return (period/40); } void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_test_unit_ready *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_test_unit_ready *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = TEST_UNIT_READY; } void scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), void *data_ptr, u_int8_t dxfer_len, u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_request_sense *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, data_ptr, dxfer_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_request_sense *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = REQUEST_SENSE; } void scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t *inq_buf, u_int32_t inq_len, int evpd, u_int8_t page_code, u_int8_t sense_len, u_int32_t timeout) { struct scsi_inquiry *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/inq_buf, /*dxfer_len*/inq_len, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_inquiry *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = INQUIRY; if (evpd) { scsi_cmd->byte2 |= SI_EVPD; scsi_cmd->page_code = page_code; } /* * A 'transfer units' count of 256 is coded as * zero for all commands with a single byte count * field. */ if (inq_len == 256) inq_len = 0; scsi_cmd->length = inq_len; } void scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int dbd, u_int8_t page_code, u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_sense_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_6; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = page_code | page; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_sense_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_sense_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SENSE_10; if (dbd != 0) scsi_cmd->byte2 |= SMS_DBD; scsi_cmd->page = page_code | page; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_IN, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation. */ if (param_len < 256) { /* * We can fit in a 6 byte cdb. */ struct scsi_mode_select_6 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_6 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_6; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_cmd->length = param_len; cdb_len = sizeof(*scsi_cmd); } else { /* * Need a 10 byte cdb. */ struct scsi_mode_select_10 *scsi_cmd; scsi_cmd = (struct scsi_mode_select_10 *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = MODE_SELECT_10; if (scsi_page_fmt != 0) scsi_cmd->byte2 |= SMS_PF; if (save_pages != 0) scsi_cmd->byte2 |= SMS_SP; scsi_ulto2b(param_len, scsi_cmd->length); cdb_len = sizeof(*scsi_cmd); } cam_fill_csio(csio, retries, cbfcnp, CAM_DIR_OUT, tag_action, param_buf, param_len, sense_len, cdb_len, timeout); } /* XXX allow specification of address and PMI bit and LBA */ void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, struct scsi_read_capacity_data *rcap_buf, u_int8_t sense_len, u_int32_t timeout) { struct scsi_read_capacity *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_IN, tag_action, /*data_ptr*/(u_int8_t *)rcap_buf, /*dxfer_len*/sizeof(*rcap_buf), sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_read_capacity *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = READ_CAPACITY; } /* * Prevent or allow the user to remove the media */ void scsi_prevent(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t action, u_int8_t sense_len, u_int32_t timeout) { struct scsi_prevent *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_prevent *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = PREVENT_ALLOW; scsi_cmd->how = action; } /* * Syncronize the media to the contents of the cache for * the given lba/count pair. Specifying 0/0 means sync * the whole cache. */ void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t begin_lba, u_int16_t lb_count, u_int8_t sense_len, u_int32_t timeout) { struct scsi_sync_cache *scsi_cmd; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); scsi_cmd = (struct scsi_sync_cache *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = SYNCHRONIZE_CACHE; scsi_ulto4b(begin_lba, scsi_cmd->begin_lba); scsi_ulto2b(lb_count, scsi_cmd->lb_count); } void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int readop, u_int8_t byte2, int minimum_cmd_size, u_int32_t lba, u_int32_t block_count, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout) { u_int8_t cdb_len; /* * Use the smallest possible command to perform the operation * as some legacy hardware does not support the 10 byte * commands. If any of the lower 5 bits in byte2 is set, we have * to go with a larger command. * */ if ((minimum_cmd_size < 10) && ((lba & 0x1fffff) == lba) && ((block_count & 0xff) == block_count) && ((byte2 & 0xe0) == 0)) { /* * We can fit in a 6 byte cdb. */ struct scsi_rw_6 *scsi_cmd; scsi_cmd = (struct scsi_rw_6 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_6 : WRITE_6; scsi_ulto3b(lba, scsi_cmd->addr); scsi_cmd->length = block_count & 0xff; scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("6byte: %x%x%x:%d:%d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->length, dxfer_len)); } else if ((minimum_cmd_size < 12) && ((block_count & 0xffff) == block_count)) { /* * Need a 10 byte cdb. */ struct scsi_rw_10 *scsi_cmd; scsi_cmd = (struct scsi_rw_10 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_10 : WRITE_10; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto2b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("10byte: %x%x%x%x:%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], dxfer_len)); } else { /* * The block count is too big for a 10 byte CDB, use a 12 * byte CDB. READ/WRITE(12) are currently only defined for * optical devices. */ struct scsi_rw_12 *scsi_cmd; scsi_cmd = (struct scsi_rw_12 *)&csio->cdb_io.cdb_bytes; scsi_cmd->opcode = readop ? READ_12 : WRITE_12; scsi_cmd->byte2 = byte2; scsi_ulto4b(lba, scsi_cmd->addr); scsi_cmd->reserved = 0; scsi_ulto4b(block_count, scsi_cmd->length); scsi_cmd->control = 0; cdb_len = sizeof(*scsi_cmd); CAM_DEBUG(csio->ccb_h.path, CAM_DEBUG_SUBTRACE, ("12byte: %x%x%x%x:%x%x%x%x: %d\n", scsi_cmd->addr[0], scsi_cmd->addr[1], scsi_cmd->addr[2], scsi_cmd->addr[3], scsi_cmd->length[0], scsi_cmd->length[1], scsi_cmd->length[2], scsi_cmd->length[3], dxfer_len)); } cam_fill_csio(csio, retries, cbfcnp, /*flags*/readop ? CAM_DIR_IN : CAM_DIR_OUT, tag_action, data_ptr, dxfer_len, sense_len, cdb_len, timeout); } void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout) { struct scsi_start_stop_unit *scsi_cmd; int extra_flags = 0; scsi_cmd = (struct scsi_start_stop_unit *)&csio->cdb_io.cdb_bytes; bzero(scsi_cmd, sizeof(*scsi_cmd)); scsi_cmd->opcode = START_STOP_UNIT; if (start != 0) { scsi_cmd->how |= SSS_START; /* it takes a lot of power to start a drive */ extra_flags |= CAM_HIGH_POWER; } if (load_eject != 0) scsi_cmd->how |= SSS_LOEJ; if (immediate != 0) scsi_cmd->byte2 |= SSS_IMMED; cam_fill_csio(csio, retries, cbfcnp, /*flags*/CAM_DIR_NONE | extra_flags, tag_action, /*data_ptr*/NULL, /*dxfer_len*/0, sense_len, sizeof(*scsi_cmd), timeout); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } /* * Try make as good a match as possible with * available sub drivers */ int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry) { struct scsi_static_inquiry_pattern *entry; struct scsi_inquiry_data *inq; entry = (struct scsi_static_inquiry_pattern *)table_entry; inq = (struct scsi_inquiry_data *)inqbuffer; if (((SID_TYPE(inq) == entry->type) || (entry->type == T_ANY)) && (SID_IS_REMOVABLE(inq) ? entry->media_type & SIP_MEDIA_REMOVABLE : entry->media_type & SIP_MEDIA_FIXED) && (cam_strmatch(inq->vendor, entry->vendor, sizeof(inq->vendor)) == 0) && (cam_strmatch(inq->product, entry->product, sizeof(inq->product)) == 0) && (cam_strmatch(inq->revision, entry->revision, sizeof(inq->revision)) == 0)) { return (0); } return (-1); } diff --git a/sys/cam/scsi/scsi_all.h b/sys/cam/scsi/scsi_all.h index 37caa19e6f52..083b83686ba9 100644 --- a/sys/cam/scsi/scsi_all.h +++ b/sys/cam/scsi/scsi_all.h @@ -1,987 +1,992 @@ /* * Largely written by Julian Elischer (julian@tfs.com) * for TRW Financial Systems. * * TRW Financial Systems, in accordance with their agreement with Carnegie * Mellon University, makes this software available to CMU to distribute * or use in any manner that they see fit as long as this message is kept with * the software. For this reason TFS also grants any other persons or * organisations permission to use or modify this software. * * TFS supplies this software to be publicly redistributed * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 * * $FreeBSD$ */ /* * SCSI general interface description */ #ifndef _SCSI_SCSI_ALL_H #define _SCSI_SCSI_ALL_H 1 #include #ifdef _KERNEL #include "opt_scsi.h" /* * This is the number of seconds we wait for devices to settle after a SCSI * bus reset. */ #ifndef SCSI_DELAY #define SCSI_DELAY 2000 #endif /* * If someone sets this to 0, we assume that they want the minimum * allowable bus settle delay. All devices need _some_ sort of bus settle * delay, so we'll set it to a minimum value of 100ms. */ #if (SCSI_DELAY == 0) #undef SCSI_DELAY #define SCSI_DELAY 100 #endif /* * Make sure the user isn't using seconds instead of milliseconds. */ #if (SCSI_DELAY < 100) #error "SCSI_DELAY is in milliseconds, not seconds! Please use a larger value" #endif #endif /* _KERNEL */ /* * SCSI command format */ /* * Define dome bits that are in ALL (or a lot of) scsi commands */ #define SCSI_CTL_LINK 0x01 #define SCSI_CTL_FLAG 0x02 #define SCSI_CTL_VENDOR 0xC0 #define SCSI_CMD_LUN 0xA0 /* these two should not be needed */ #define SCSI_CMD_LUN_SHIFT 5 /* LUN in the cmd is no longer SCSI */ #define SCSI_MAX_CDBLEN 16 /* * 16 byte commands are in the * SCSI-3 spec */ #if defined(CAM_MAX_CDBLEN) && (CAM_MAX_CDBLEN < SCSI_MAX_CDBLEN) #error "CAM_MAX_CDBLEN cannot be less than SCSI_MAX_CDBLEN" #endif /* * This type defines actions to be taken when a particular sense code is * received. Right now, these flags are only defined to take up 16 bits, * but can be expanded in the future if necessary. */ typedef enum { SS_NOP = 0x000000, /* Do nothing */ SS_RETRY = 0x010000, /* Retry the command */ SS_FAIL = 0x020000, /* Bail out */ SS_START = 0x030000, /* Send a Start Unit command to the device, * then retry the original command. */ SS_TUR = 0x040000, /* Send a Test Unit Ready command to the * device, then retry the original command. */ SS_MANUAL = 0x050000, /* * This error must be handled manually, * i.e. the code must look at the asc and * ascq values and determine the proper * course of action. */ SS_TURSTART = 0x060000, /* * Send a Test Unit Ready command to the * device, and if that fails, send a start * unit. */ SS_MASK = 0xff0000 } scsi_sense_action; typedef enum { SSQ_NONE = 0x0000, SSQ_DECREMENT_COUNT = 0x0100, /* Decrement the retry count */ SSQ_MANY = 0x0200, /* send lots of recovery commands */ SSQ_RANGE = 0x0400, /* * Yes, this is a hack. Basically, * if this flag is set then it * represents an ascq range. The * "correct" way to implement the * ranges might be to add a special * field to the sense code table, * but that would take up a lot of * additional space. This solution * isn't as elegant, but is more * space efficient. */ SSQ_PRINT_SENSE = 0x0800, SSQ_MASK = 0xff00 } scsi_sense_action_qualifier; /* Mask for error status values */ #define SS_ERRMASK 0xff /* The default error action */ #define SS_DEF SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE|EIO /* Default error action, without an error return value */ #define SS_NEDEF SS_RETRY|SSQ_DECREMENT_COUNT|SSQ_PRINT_SENSE /* Default error action, without sense printing or an error return value */ #define SS_NEPDEF SS_RETRY|SSQ_DECREMENT_COUNT struct scsi_generic { u_int8_t opcode; u_int8_t bytes[11]; }; struct scsi_request_sense { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[2]; u_int8_t length; u_int8_t control; }; struct scsi_test_unit_ready { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[3]; u_int8_t control; }; struct scsi_send_diag { u_int8_t opcode; u_int8_t byte2; #define SSD_UOL 0x01 #define SSD_DOL 0x02 #define SSD_SELFTEST 0x04 #define SSD_PF 0x10 u_int8_t unused[1]; u_int8_t paramlen[2]; u_int8_t control; }; struct scsi_sense { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[2]; u_int8_t length; u_int8_t control; }; struct scsi_inquiry { u_int8_t opcode; u_int8_t byte2; #define SI_EVPD 0x01 u_int8_t page_code; u_int8_t reserved; u_int8_t length; u_int8_t control; }; struct scsi_mode_sense_6 { u_int8_t opcode; u_int8_t byte2; #define SMS_DBD 0x08 u_int8_t page; #define SMS_PAGE_CODE 0x3F #define SMS_VENDOR_SPECIFIC_PAGE 0x00 #define SMS_DISCONNECT_RECONNECT_PAGE 0x02 #define SMS_PERIPHERAL_DEVICE_PAGE 0x09 #define SMS_CONTROL_MODE_PAGE 0x0A #define SMS_ALL_PAGES_PAGE 0x3F #define SMS_PAGE_CTRL_MASK 0xC0 #define SMS_PAGE_CTRL_CURRENT 0x00 #define SMS_PAGE_CTRL_CHANGEABLE 0x40 #define SMS_PAGE_CTRL_DEFAULT 0x80 #define SMS_PAGE_CTRL_SAVED 0xC0 u_int8_t unused; u_int8_t length; u_int8_t control; }; struct scsi_mode_sense_10 { u_int8_t opcode; u_int8_t byte2; /* same bits as small version */ u_int8_t page; /* same bits as small version */ u_int8_t unused[4]; u_int8_t length[2]; u_int8_t control; }; struct scsi_mode_select_6 { u_int8_t opcode; u_int8_t byte2; #define SMS_SP 0x01 #define SMS_PF 0x10 u_int8_t unused[2]; u_int8_t length; u_int8_t control; }; struct scsi_mode_select_10 { u_int8_t opcode; u_int8_t byte2; /* same bits as small version */ u_int8_t unused[5]; u_int8_t length[2]; u_int8_t control; }; /* * When sending a mode select to a tape drive, the medium type must be 0. */ struct scsi_mode_hdr_6 { u_int8_t datalen; u_int8_t medium_type; u_int8_t dev_specific; u_int8_t block_descr_len; }; struct scsi_mode_hdr_10 { u_int8_t datalen[2]; u_int8_t medium_type; u_int8_t dev_specific; u_int8_t reserved[2]; u_int8_t block_descr_len[2]; }; struct scsi_mode_block_descr { u_int8_t density_code; u_int8_t num_blocks[3]; u_int8_t reserved; u_int8_t block_len[3]; }; struct scsi_control_page { u_int8_t page_code; u_int8_t page_length; u_int8_t rlec; #define SCB_RLEC 0x01 /*Report Log Exception Cond*/ u_int8_t queue_flags; #define SCP_QUEUE_ALG_MASK 0xF0 #define SCP_QUEUE_ALG_RESTRICTED 0x00 #define SCP_QUEUE_ALG_UNRESTRICTED 0x10 #define SCP_QUEUE_ERR 0x02 /*Queued I/O aborted for CACs*/ #define SCP_QUEUE_DQUE 0x01 /*Queued I/O disabled*/ u_int8_t eca_and_aen; #define SCP_EECA 0x80 /*Enable Extended CA*/ #define SCP_RAENP 0x04 /*Ready AEN Permission*/ #define SCP_UAAENP 0x02 /*UA AEN Permission*/ #define SCP_EAENP 0x01 /*Error AEN Permission*/ u_int8_t reserved; u_int8_t aen_holdoff_period[2]; }; struct scsi_reserve { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[2]; u_int8_t length; u_int8_t control; }; struct scsi_release { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[2]; u_int8_t length; u_int8_t control; }; struct scsi_prevent { u_int8_t opcode; u_int8_t byte2; u_int8_t unused[2]; u_int8_t how; u_int8_t control; }; #define PR_PREVENT 0x01 #define PR_ALLOW 0x00 struct scsi_sync_cache { u_int8_t opcode; u_int8_t byte2; u_int8_t begin_lba[4]; u_int8_t reserved; u_int8_t lb_count[2]; u_int8_t control; }; struct scsi_changedef { u_int8_t opcode; u_int8_t byte2; u_int8_t unused1; u_int8_t how; u_int8_t unused[4]; u_int8_t datalen; u_int8_t control; }; struct scsi_read_buffer { u_int8_t opcode; u_int8_t byte2; #define RWB_MODE 0x07 #define RWB_MODE_HDR_DATA 0x00 #define RWB_MODE_DATA 0x02 #define RWB_MODE_DOWNLOAD 0x04 #define RWB_MODE_DOWNLOAD_SAVE 0x05 u_int8_t buffer_id; u_int8_t offset[3]; u_int8_t length[3]; u_int8_t control; }; struct scsi_write_buffer { u_int8_t opcode; u_int8_t byte2; u_int8_t buffer_id; u_int8_t offset[3]; u_int8_t length[3]; u_int8_t control; }; struct scsi_rw_6 { u_int8_t opcode; u_int8_t addr[3]; /* only 5 bits are valid in the MSB address byte */ #define SRW_TOPADDR 0x1F u_int8_t length; u_int8_t control; }; struct scsi_rw_10 { u_int8_t opcode; #define SRW10_RELADDR 0x01 #define SRW10_FUA 0x08 #define SRW10_DPO 0x10 u_int8_t byte2; u_int8_t addr[4]; u_int8_t reserved; u_int8_t length[2]; u_int8_t control; }; struct scsi_rw_12 { u_int8_t opcode; #define SRW12_RELADDR 0x01 #define SRW12_FUA 0x08 #define SRW12_DPO 0x10 u_int8_t byte2; u_int8_t addr[4]; u_int8_t reserved; u_int8_t length[4]; u_int8_t control; }; struct scsi_start_stop_unit { u_int8_t opcode; u_int8_t byte2; #define SSS_IMMED 0x01 u_int8_t reserved[2]; u_int8_t how; #define SSS_START 0x01 #define SSS_LOEJ 0x02 u_int8_t control; }; #define SC_SCSI_1 0x01 #define SC_SCSI_2 0x03 /* * Opcodes */ #define TEST_UNIT_READY 0x00 #define REQUEST_SENSE 0x03 #define READ_6 0x08 #define WRITE_6 0x0a #define INQUIRY 0x12 #define MODE_SELECT_6 0x15 #define MODE_SENSE_6 0x1a #define START_STOP_UNIT 0x1b #define START_STOP 0x1b #define RESERVE 0x16 #define RELEASE 0x17 #define RECEIVE_DIAGNOSTIC 0x1c #define SEND_DIAGNOSTIC 0x1d #define PREVENT_ALLOW 0x1e #define READ_CAPACITY 0x25 #define READ_10 0x28 #define WRITE_10 0x2a #define POSITION_TO_ELEMENT 0x2b #define SYNCHRONIZE_CACHE 0x35 #define WRITE_BUFFER 0x3b #define READ_BUFFER 0x3c #define CHANGE_DEFINITION 0x40 #define MODE_SELECT_10 0x55 #define MODE_SENSE_10 0x5A #define MOVE_MEDIUM 0xa5 #define READ_12 0xa8 #define WRITE_12 0xaa #define READ_ELEMENT_STATUS 0xb8 /* * Device Types */ #define T_DIRECT 0x00 #define T_SEQUENTIAL 0x01 #define T_PRINTER 0x02 #define T_PROCESSOR 0x03 #define T_WORM 0x04 #define T_CDROM 0x05 #define T_SCANNER 0x06 #define T_OPTICAL 0x07 #define T_CHANGER 0x08 #define T_COMM 0x09 #define T_ASC0 0x0a #define T_ASC1 0x0b #define T_STORARRAY 0x0c #define T_ENCLOSURE 0x0d #define T_RBC 0x0e #define T_OCRW 0x0f #define T_NODEVICE 0x1F #define T_ANY 0xFF /* Used in Quirk table matches */ #define T_REMOV 1 #define T_FIXED 0 - +/* + * This length is the initial inquiry length used by the probe code, as + * well as the legnth necessary for scsi_print_inquiry() to function + * correctly. If either use requires a different length in the future, + * the two values should be de-coupled. + */ #define SHORT_INQUIRY_LENGTH 36 struct scsi_inquiry_data { u_int8_t device; #define SID_TYPE(inq_data) ((inq_data)->device & 0x1f) #define SID_QUAL(inq_data) (((inq_data)->device & 0xE0) >> 5) #define SID_QUAL_LU_CONNECTED 0x00 /* The specified peripheral device * type is currently connected to * logical unit. If the target cannot * determine whether or not a physical * device is currently connected, it * shall also use this peripheral * qualifier when returning the INQUIRY * data. This peripheral qualifier * does not mean that the device is * ready for access by the initiator. */ #define SID_QUAL_LU_OFFLINE 0x01 /* The target is capable of supporting * the specified peripheral device type * on this logical unit; however, the * physical device is not currently * connected to this logical unit. */ #define SID_QUAL_RSVD 0x02 #define SID_QUAL_BAD_LU 0x03 /* The target is not capable of * supporting a physical device on * this logical unit. For this * peripheral qualifier the peripheral * device type shall be set to 1Fh to * provide compatibility with previous * versions of SCSI. All other * peripheral device type values are * reserved for this peripheral * qualifier. */ #define SID_QUAL_IS_VENDOR_UNIQUE(inq_data) ((SID_QUAL(inq_data) & 0x08) != 0) u_int8_t dev_qual2; #define SID_QUAL2 0x7F #define SID_IS_REMOVABLE(inq_data) (((inq_data)->dev_qual2 & 0x80) != 0) u_int8_t version; #define SID_ANSI_REV(inq_data) ((inq_data)->version & 0x07) #define SCSI_REV_0 0 #define SCSI_REV_CCS 1 #define SCSI_REV_2 2 #define SCSI_REV_3 3 #define SCSI_REV_SPC2 4 #define SID_ECMA 0x38 #define SID_ISO 0xC0 u_int8_t response_format; #define SID_AENC 0x80 #define SID_TrmIOP 0x40 u_int8_t additional_length; u_int8_t reserved[2]; u_int8_t flags; #define SID_SftRe 0x01 #define SID_CmdQue 0x02 #define SID_Linked 0x08 #define SID_Sync 0x10 #define SID_WBus16 0x20 #define SID_WBus32 0x40 #define SID_RelAdr 0x80 #define SID_VENDOR_SIZE 8 char vendor[SID_VENDOR_SIZE]; #define SID_PRODUCT_SIZE 16 char product[SID_PRODUCT_SIZE]; #define SID_REVISION_SIZE 4 char revision[SID_REVISION_SIZE]; /* * The following fields were taken from SCSI Primary Commands - 2 * (SPC-2) Revision 14, Dated 11 November 1999 */ #define SID_VENDOR_SPECIFIC_0_SIZE 20 u_int8_t vendor_specific0[SID_VENDOR_SPECIFIC_0_SIZE]; /* * An extension of SCSI Parallel Specific Values */ #define SID_SPI_IUS 0x01 #define SID_SPI_QAS 0x02 #define SID_SPI_CLOCK_ST 0x00 #define SID_SPI_CLOCK_DT 0x04 #define SID_SPI_CLOCK_DT_ST 0x0C u_int8_t spi3data; u_int8_t reserved2; /* * Version Descriptors, stored 2 byte values. */ u_int8_t version1[2]; u_int8_t version2[2]; u_int8_t version3[2]; u_int8_t version4[2]; u_int8_t version5[2]; u_int8_t version6[2]; u_int8_t version7[2]; u_int8_t version8[2]; u_int8_t reserved3[22]; #define SID_VENDOR_SPECIFIC_1_SIZE 160 u_int8_t vendor_specific1[SID_VENDOR_SPECIFIC_1_SIZE]; }; struct scsi_vpd_unit_serial_number { u_int8_t device; u_int8_t page_code; #define SVPD_UNIT_SERIAL_NUMBER 0x80 u_int8_t reserved; u_int8_t length; /* serial number length */ #define SVPD_SERIAL_NUM_SIZE 251 u_int8_t serial_num[SVPD_SERIAL_NUM_SIZE]; }; struct scsi_read_capacity { u_int8_t opcode; u_int8_t byte2; u_int8_t addr[4]; u_int8_t unused[3]; u_int8_t control; }; struct scsi_read_capacity_data { u_int8_t addr[4]; u_int8_t length[4]; }; struct scsi_sense_data { u_int8_t error_code; #define SSD_ERRCODE 0x7F #define SSD_CURRENT_ERROR 0x70 #define SSD_DEFERRED_ERROR 0x71 #define SSD_ERRCODE_VALID 0x80 u_int8_t segment; u_int8_t flags; #define SSD_KEY 0x0F #define SSD_KEY_NO_SENSE 0x00 #define SSD_KEY_RECOVERED_ERROR 0x01 #define SSD_KEY_NOT_READY 0x02 #define SSD_KEY_MEDIUM_ERROR 0x03 #define SSD_KEY_HARDWARE_ERROR 0x04 #define SSD_KEY_ILLEGAL_REQUEST 0x05 #define SSD_KEY_UNIT_ATTENTION 0x06 #define SSD_KEY_DATA_PROTECT 0x07 #define SSD_KEY_BLANK_CHECK 0x08 #define SSD_KEY_Vendor_Specific 0x09 #define SSD_KEY_COPY_ABORTED 0x0a #define SSD_KEY_ABORTED_COMMAND 0x0b #define SSD_KEY_EQUAL 0x0c #define SSD_KEY_VOLUME_OVERFLOW 0x0d #define SSD_KEY_MISCOMPARE 0x0e #define SSD_KEY_RESERVED 0x0f #define SSD_ILI 0x20 #define SSD_EOM 0x40 #define SSD_FILEMARK 0x80 u_int8_t info[4]; u_int8_t extra_len; u_int8_t cmd_spec_info[4]; u_int8_t add_sense_code; u_int8_t add_sense_code_qual; u_int8_t fru; u_int8_t sense_key_spec[3]; #define SSD_SCS_VALID 0x80 #define SSD_FIELDPTR_CMD 0x40 #define SSD_BITPTR_VALID 0x08 #define SSD_BITPTR_VALUE 0x07 #define SSD_MIN_SIZE 18 u_int8_t extra_bytes[14]; #define SSD_FULL_SIZE sizeof(struct scsi_sense_data) }; struct scsi_mode_header_6 { u_int8_t data_length; /* Sense data length */ u_int8_t medium_type; u_int8_t dev_spec; u_int8_t blk_desc_len; }; struct scsi_mode_header_10 { u_int8_t data_length[2];/* Sense data length */ u_int8_t medium_type; u_int8_t dev_spec; u_int8_t unused[2]; u_int8_t blk_desc_len[2]; }; struct scsi_mode_blk_desc { u_int8_t density; u_int8_t nblocks[3]; u_int8_t reserved; u_int8_t blklen[3]; }; #define SCSI_DEFAULT_DENSITY 0x00 /* use 'default' density */ #define SCSI_SAME_DENSITY 0x7f /* use 'same' density- >= SCSI-2 only */ /* * Status Byte */ #define SCSI_STATUS_OK 0x00 #define SCSI_STATUS_CHECK_COND 0x02 #define SCSI_STATUS_COND_MET 0x04 #define SCSI_STATUS_BUSY 0x08 #define SCSI_STATUS_INTERMED 0x10 #define SCSI_STATUS_INTERMED_COND_MET 0x14 #define SCSI_STATUS_RESERV_CONFLICT 0x18 #define SCSI_STATUS_CMD_TERMINATED 0x22 #define SCSI_STATUS_QUEUE_FULL 0x28 struct scsi_inquiry_pattern { u_int8_t type; u_int8_t media_type; #define SIP_MEDIA_REMOVABLE 0x01 #define SIP_MEDIA_FIXED 0x02 const char *vendor; const char *product; const char *revision; }; struct scsi_static_inquiry_pattern { u_int8_t type; u_int8_t media_type; char vendor[SID_VENDOR_SIZE+1]; char product[SID_PRODUCT_SIZE+1]; char revision[SID_REVISION_SIZE+1]; }; struct scsi_sense_quirk_entry { struct scsi_inquiry_pattern inq_pat; int num_ascs; struct asc_table_entry *asc_info; }; struct asc_table_entry { u_int8_t asc; u_int8_t ascq; u_int32_t action; #if !defined(SCSI_NO_SENSE_STRINGS) const char *desc; #endif }; struct op_table_entry { u_int8_t opcode; u_int16_t opmask; const char *desc; }; struct scsi_op_quirk_entry { struct scsi_inquiry_pattern inq_pat; int num_ops; struct op_table_entry *op_table; }; struct ccb_scsiio; struct cam_periph; union ccb; #ifndef _KERNEL struct cam_device; #endif extern const char *scsi_sense_key_text[]; __BEGIN_DECLS const char * scsi_sense_desc(int asc, int ascq, struct scsi_inquiry_data *inq_data); scsi_sense_action scsi_error_action(int asc, int ascq, struct scsi_inquiry_data *inq_data); #ifdef _KERNEL void scsi_sense_print(struct ccb_scsiio *csio); int scsi_interpret_sense(union ccb *ccb, u_int32_t sense_flags, u_int32_t *relsim_flags, u_int32_t *reduction, u_int32_t *timeout, scsi_sense_action error_action); #else char * scsi_sense_string(struct cam_device *device, struct ccb_scsiio *csio, char *str, int str_len); void scsi_sense_print(struct cam_device *device, struct ccb_scsiio *csio, FILE *ofile); int scsi_interpret_sense(struct cam_device *device, union ccb *ccb, u_int32_t sense_flags, u_int32_t *relsim_flags, u_int32_t *reduction, u_int32_t *timeout, scsi_sense_action error_action); #endif /* _KERNEL */ #define SF_RETRY_UA 0x01 #define SF_NO_PRINT 0x02 #define SF_QUIET_IR 0x04 /* Be quiet about Illegal Request reponses */ #define SF_PRINT_ALWAYS 0x08 #define SF_RETRY_SELTO 0x10 /* Retry selection timeouts */ const char * scsi_op_desc(u_int16_t opcode, struct scsi_inquiry_data *inq_data); char * scsi_cdb_string(u_int8_t *cdb_ptr, char *cdb_string, size_t len); void scsi_print_inquiry(struct scsi_inquiry_data *inq_data); u_int scsi_calc_syncsrate(u_int period_factor); u_int scsi_calc_syncparam(u_int period); void scsi_test_unit_ready(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout); void scsi_request_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), void *data_ptr, u_int8_t dxfer_len, u_int8_t tag_action, u_int8_t sense_len, u_int32_t timeout); void scsi_inquiry(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t *inq_buf, u_int32_t inq_len, int evpd, u_int8_t page_code, u_int8_t sense_len, u_int32_t timeout); void scsi_mode_sense(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int dbd, u_int8_t page_code, u_int8_t page, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); void scsi_mode_select(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int scsi_page_fmt, int save_pages, u_int8_t *param_buf, u_int32_t param_len, u_int8_t sense_len, u_int32_t timeout); void scsi_read_capacity(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, struct scsi_read_capacity_data *rcap_buf, u_int8_t sense_len, u_int32_t timeout); void scsi_prevent(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int8_t action, u_int8_t sense_len, u_int32_t timeout); void scsi_synchronize_cache(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, u_int32_t begin_lba, u_int16_t lb_count, u_int8_t sense_len, u_int32_t timeout); void scsi_read_write(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int readop, u_int8_t byte2, int minimum_cmd_size, u_int32_t lba, u_int32_t block_count, u_int8_t *data_ptr, u_int32_t dxfer_len, u_int8_t sense_len, u_int32_t timeout); void scsi_start_stop(struct ccb_scsiio *csio, u_int32_t retries, void (*cbfcnp)(struct cam_periph *, union ccb *), u_int8_t tag_action, int start, int load_eject, int immediate, u_int8_t sense_len, u_int32_t timeout); int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry); int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry); static __inline void scsi_extract_sense(struct scsi_sense_data *sense, int *error_code, int *sense_key, int *asc, int *ascq); static __inline void scsi_ulto2b(u_int32_t val, u_int8_t *bytes); static __inline void scsi_ulto3b(u_int32_t val, u_int8_t *bytes); static __inline void scsi_ulto4b(u_int32_t val, u_int8_t *bytes); static __inline u_int32_t scsi_2btoul(u_int8_t *bytes); static __inline u_int32_t scsi_3btoul(u_int8_t *bytes); static __inline int32_t scsi_3btol(u_int8_t *bytes); static __inline u_int32_t scsi_4btoul(u_int8_t *bytes); static __inline void *find_mode_page_6(struct scsi_mode_header_6 *mode_header); static __inline void *find_mode_page_10(struct scsi_mode_header_10 *mode_header); static __inline void scsi_extract_sense(struct scsi_sense_data *sense, int *error_code, int *sense_key, int *asc, int *ascq) { *error_code = sense->error_code & SSD_ERRCODE; *sense_key = sense->flags & SSD_KEY; *asc = (sense->extra_len >= 5) ? sense->add_sense_code : 0; *ascq = (sense->extra_len >= 6) ? sense->add_sense_code_qual : 0; } static __inline void scsi_ulto2b(u_int32_t val, u_int8_t *bytes) { bytes[0] = (val >> 8) & 0xff; bytes[1] = val & 0xff; } static __inline void scsi_ulto3b(u_int32_t val, u_int8_t *bytes) { bytes[0] = (val >> 16) & 0xff; bytes[1] = (val >> 8) & 0xff; bytes[2] = val & 0xff; } static __inline void scsi_ulto4b(u_int32_t val, u_int8_t *bytes) { bytes[0] = (val >> 24) & 0xff; bytes[1] = (val >> 16) & 0xff; bytes[2] = (val >> 8) & 0xff; bytes[3] = val & 0xff; } static __inline u_int32_t scsi_2btoul(u_int8_t *bytes) { u_int32_t rv; rv = (bytes[0] << 8) | bytes[1]; return (rv); } static __inline u_int32_t scsi_3btoul(u_int8_t *bytes) { u_int32_t rv; rv = (bytes[0] << 16) | (bytes[1] << 8) | bytes[2]; return (rv); } static __inline int32_t scsi_3btol(u_int8_t *bytes) { u_int32_t rc = scsi_3btoul(bytes); if (rc & 0x00800000) rc |= 0xff000000; return (int32_t) rc; } static __inline u_int32_t scsi_4btoul(u_int8_t *bytes) { u_int32_t rv; rv = (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3]; return (rv); } /* * Given the pointer to a returned mode sense buffer, return a pointer to * the start of the first mode page. */ static __inline void * find_mode_page_6(struct scsi_mode_header_6 *mode_header) { void *page_start; page_start = (void *)((u_int8_t *)&mode_header[1] + mode_header->blk_desc_len); return(page_start); } static __inline void * find_mode_page_10(struct scsi_mode_header_10 *mode_header) { void *page_start; page_start = (void *)((u_int8_t *)&mode_header[1] + scsi_2btoul(mode_header->blk_desc_len)); return(page_start); } __END_DECLS #endif /*_SCSI_SCSI_ALL_H*/