*** src/sbin/camcontrol/camcontrol.8.orig --- src/sbin/camcontrol/camcontrol.8 *************** *** 27,33 **** .\" .\" $FreeBSD$ .\" ! .Dd March 19, 2015 .Dt CAMCONTROL 8 .Os .Sh NAME --- 27,33 ---- .\" .\" $FreeBSD$ .\" ! .Dd August 6, 2015 .Dt CAMCONTROL 8 .Os .Sh NAME *************** *** 253,260 **** .Op device id .Op generic args .Aq Fl f Ar fw_image .Op Fl y - .Op Fl s .Nm .Ic security .Op device id --- 253,261 ---- .Op device id .Op generic args .Aq Fl f Ar fw_image + .Op Fl q + .Op Fl s .Op Fl y .Nm .Ic security .Op device id *************** *** 311,316 **** --- 312,325 ---- .Op Fl T Ar elem_type .Op Fl V Ar lv_num .Nm + .Ic opcodes + .Op device id + .Op generic args + .Op Fl o Ar opcode + .Op Fl s Ar service_action + .Op Fl N + .Op Fl T + .Nm .Ic help .Sh DESCRIPTION The *************** *** 1516,1539 **** The password for all HPA commands is limited to 32 characters, longer passwords will fail. .It Ic fwdownload ! Program firmware of the named SCSI device using the image file provided. .Pp ! Current list of supported vendors: ! .Bl -bullet -offset indent -compact ! .It ! HITACHI ! .It ! HP ! .It ! IBM ! .It ! PLEXTOR ! .It ! QUANTUM ! .It ! SAMSUNG ! .It ! SEAGATE .El .Pp .Em WARNING! WARNING! WARNING! --- 1525,1565 ---- The password for all HPA commands is limited to 32 characters, longer passwords will fail. .It Ic fwdownload ! Program firmware of the named ! .Tn SCSI ! or ATA device using the image file provided. ! .Pp ! If the device is a ! .Tn SCSI ! device and it provides a recommended timeout for the WRITE BUFFER command ! (see the ! .Nm ! opcodes subcommand), that timeout will be used for the firmware download. ! The drive-recommended timeout value may be overridden on the command line ! with the ! .Fl t ! option. .Pp ! Current list of supported vendors for SCSI/SAS drives: ! .Bl -tag -width 10n ! .It HGST ! Tested with 4TB SAS drives, model number HUS724040ALS640. ! .It HITACHI ! .It HP ! .It IBM ! Tested with LTO-5 (ULTRIUM-HH5) and LTO-6 (ULTRIUM-HH6) tape drives. ! There is a separate table entry for hard drives, because the update method ! for hard drives is different than the method for tape drives. ! .It PLEXTOR ! .It QUALSTAR ! .It QUANTUM ! .It SAMSUNG ! Tested with SM1625 SSDs. ! .It SEAGATE ! Tested with Constellation ES (ST32000444SS), ES.2 (ST33000651SS) and ! ES.3 (ST1000NM0023) drives. ! .It SmrtStor ! Tested with 400GB Optimus SSDs (TXA2D20400GA6001). .El .Pp .Em WARNING! WARNING! WARNING! *************** *** 1547,1569 **** guarantee it will not break a device from the listed vendors. Ensure that you have a recent backup of the data on the device before performing a firmware update. .Bl -tag -width 11n .It Fl f Ar fw_image Path to the firmware image file to be downloaded to the specified device. ! .It Fl y ! Do not ask for confirmation. .It Fl s Run in simulation mode. ! Packet sizes that will be sent are shown, but no actual packet is sent to the ! device. ! No confirmation is asked in simulation mode. .It Fl v ! Besides showing sense information in case of a failure, the verbose option ! causes ! .Nm ! to output a line for every firmware segment that is sent to the device by the ! fwdownload command ! -- the same as the ones shown in simulation mode. .El .It Ic persist Persistent reservation support. --- 1573,1653 ---- guarantee it will not break a device from the listed vendors. Ensure that you have a recent backup of the data on the device before performing a firmware update. + .Pp + Note that unknown + .Tn SCSI + protocol devices will not be programmed, since there is little chance of + the firmware download succeeding. + .Pp + .Nm + will currently attempt a firmware download to any + .Tn ATA + or + .Tn SATA + device, since the standard + .Tn ATA + DOWNLOAD MICROCODE command may work. + Firmware downloads to + .Tn ATA + and + .Tn SATA + devices are supported for devices connected + to standard + .Tn ATA + and + .Tn SATA + controllers, and devices connected to SAS controllers + with + .Tn SCSI + to + .Tn ATA + translation capability. + In the latter case, + .Nm + uses the + .Tn SCSI + .Tn ATA + PASS-THROUGH command to send the + .Tn ATA + DOWNLOAD MICROCODE command to the drive. + Some + .Tn SCSI + to + .Tn ATA + translation implementations don't work fully when translating + .Tn SCSI + WRITE BUFFER commands to + .Tn ATA + DOWNLOAD MICROCODE commands, but do support + .Tn ATA + passthrough well enough to do a firmware download. .Bl -tag -width 11n .It Fl f Ar fw_image Path to the firmware image file to be downloaded to the specified device. ! .It Fl q ! Do not print informational messages, only print errors. ! This option should be used with the ! .Fl y ! option to suppress all output. .It Fl s Run in simulation mode. ! Device checks are run and the confirmation dialog is shown, but no firmware ! download will occur. .It Fl v ! Show ! .Tn SCSI ! or ! .Tn ATA ! errors in the event of a failure. ! .Pp ! In simulation mode, print out the ! .Tn SCSI ! CDB ! or ! .Tn ATA ! register values that would be used for the firmware download command. ! .It Fl y ! Do not ask for confirmation. .El .It Ic persist Persistent reservation support. *************** *** 1958,1963 **** --- 2042,2093 ---- Specify the number of the logical volume to operate on. If the media has multiple logical volumes, this will allow displaying or writing attributes on the given logical volume. + .It Ic opcodes + Issue the REPORT SUPPORTED OPCODES service action of the + .Tn SCSI + MAINTENANCE IN + command. + Without arguments, this command will return a list of all + .Tn SCSI + commands supported by the device, including service actions of commands + that support service actions. + It will also include the + .Tn SCSI + CDB (Command Data Block) length for each command, and the description of + each command if it is known. + .Bl -tag -width 18n + .It Fl o Ar opcode + Request information on a specific opcode instead of the list of supported + commands. + If supported, the target will return a CDB-like structure that indicates + the opcode, service action (if any), and a mask of bits that are supported + in that CDB. + .It Fl s Ar service_action + For commands that support a service action, specify the service action to + query. + .It Fl N + If a service action is specified for a given opcode, and the device does + not support the given service action, the device should not return a + .Tn SCSI + error, but rather indicate in the returned parameter data that the command + is not supported. + By default, if a service action is specified for an opcode, and service + actions are not supported for the opcode in question, the device will + return an error. + .It Fl T + Include timeout values. + This option works with the default display, which includes all commands + supported by the device, and with the + .Fl o + and + .Fl s + options, which request information on a specific command and service + action. + This requests that the device report Nominal and Recommended timeout values + for the given command or commands. + The timeout values are in seconds. + The timeout descriptor also includes a command-specific + .El .It Ic help Print out verbose usage information. .El *** src/sbin/camcontrol/camcontrol.c.orig --- src/sbin/camcontrol/camcontrol.c *************** *** 99,105 **** CAM_CMD_PERSIST = 0x00000020, CAM_CMD_APM = 0x00000021, CAM_CMD_AAM = 0x00000022, ! CAM_CMD_ATTRIB = 0x00000023 } cam_cmdmask; typedef enum { --- 99,106 ---- CAM_CMD_PERSIST = 0x00000020, CAM_CMD_APM = 0x00000021, CAM_CMD_AAM = 0x00000022, ! CAM_CMD_ATTRIB = 0x00000023, ! CAM_CMD_OPCODES = 0x00000024 } cam_cmdmask; typedef enum { *************** *** 221,231 **** {"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""}, {"apm", CAM_CMD_APM, CAM_ARG_NONE, "l:"}, {"aam", CAM_CMD_AAM, CAM_ARG_NONE, "l:"}, ! {"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:ys"}, {"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"}, {"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"}, {"persist", CAM_CMD_PERSIST, CAM_ARG_NONE, "ai:I:k:K:o:ps:ST:U"}, {"attrib", CAM_CMD_ATTRIB, CAM_ARG_NONE, "a:ce:F:p:r:s:T:w:V:"}, #endif /* MINIMALISTIC */ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, --- 222,233 ---- {"sleep", CAM_CMD_SLEEP, CAM_ARG_NONE, ""}, {"apm", CAM_CMD_APM, CAM_ARG_NONE, "l:"}, {"aam", CAM_CMD_AAM, CAM_ARG_NONE, "l:"}, ! {"fwdownload", CAM_CMD_DOWNLOAD_FW, CAM_ARG_NONE, "f:qsy"}, {"security", CAM_CMD_SECURITY, CAM_ARG_NONE, "d:e:fh:k:l:qs:T:U:y"}, {"hpa", CAM_CMD_HPA, CAM_ARG_NONE, "Pflp:qs:U:y"}, {"persist", CAM_CMD_PERSIST, CAM_ARG_NONE, "ai:I:k:K:o:ps:ST:U"}, {"attrib", CAM_CMD_ATTRIB, CAM_ARG_NONE, "a:ce:F:p:r:s:T:w:V:"}, + {"opcodes", CAM_CMD_OPCODES, CAM_ARG_NONE, "No:s:T"}, #endif /* MINIMALISTIC */ {"help", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, {"-?", CAM_CMD_USAGE, CAM_ARG_NONE, NULL}, *************** *** 264,270 **** 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 camxferrate(struct cam_device *device); #endif /* MINIMALISTIC */ static int parse_btl(char *tstr, path_id_t *bus, target_id_t *target, lun_id_t *lun, cam_argmask *arglst); --- 266,271 ---- *************** *** 319,324 **** --- 320,333 ---- int argc, char **argv, char *combinedopt); static int atahpa(struct cam_device *device, int retry_count, int timeout, int argc, char **argv, char *combinedopt); + static int scsiprintoneopcode(struct cam_device *device, int req_opcode, + int sa_set, int req_sa, uint8_t *buf, + uint32_t valid_len); + static int scsiprintopcodes(struct cam_device *device, int td_req, uint8_t *buf, + uint32_t valid_len); + static int scsiopcodes(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout, + int verbose); #endif /* MINIMALISTIC */ #ifndef min *************** *** 1018,1024 **** return(0); } ! static int camxferrate(struct cam_device *device) { struct ccb_pathinq cpi; --- 1027,1033 ---- return(0); } ! int camxferrate(struct cam_device *device) { struct ccb_pathinq cpi; *************** *** 2034,2040 **** } ! static int ata_do_identify(struct cam_device *device, int retry_count, int timeout, union ccb *ccb, struct ata_params** ident_bufp) { --- 2043,2049 ---- } ! int ata_do_identify(struct cam_device *device, int retry_count, int timeout, union ccb *ccb, struct ata_params** ident_bufp) { *************** *** 4940,4965 **** return(retval); } ! /* return the type of disk (really the command type) */ ! static const char * ! get_disk_type(struct cam_device *device) { ! struct ccb_getdev cgd; ! (void) memset(&cgd, 0x0, sizeof(cgd)); ! get_cgd(device, &cgd); ! switch(cgd.protocol) { case PROTO_SCSI: ! return "scsi"; case PROTO_ATA: case PROTO_ATAPI: case PROTO_SATAPM: ! return "ata"; default: ! return "unknown"; } } static void cpi_print(struct ccb_pathinq *cpi) { --- 4949,5121 ---- return(retval); } ! /* ! * Returns 1 if the device has the VPD page, 0 if it does not, and -1 on an ! * error. ! */ ! int ! dev_has_vpd_page(struct cam_device *dev, uint8_t page_id, int retry_count, ! int timeout, int verbosemode) ! { ! union ccb *ccb = NULL; ! struct scsi_vpd_supported_page_list sup_pages; ! int i; ! int retval = 0; ! ! ccb = cam_getccb(dev); ! if (ccb == NULL) { ! warn("Unable to allocate CCB"); ! retval = -1; ! goto bailout; ! } ! ! /* 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)); ! ! bzero(&sup_pages, sizeof(sup_pages)); ! ! scsi_inquiry(&ccb->csio, ! /*retries*/ retry_count, ! /*cbfcnp*/ NULL, ! /* tag_action */ MSG_SIMPLE_Q_TAG, ! /* inq_buf */ (u_int8_t *)&sup_pages, ! /* inq_len */ sizeof(sup_pages), ! /* evpd */ 1, ! /* page_code */ SVPD_SUPPORTED_PAGE_LIST, ! /* sense_len */ SSD_FULL_SIZE, ! /* timeout */ timeout ? timeout : 5000); ! ! /* Disable freezing the device queue */ ! ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; ! ! if (retry_count != 0) ! ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; ! ! if (cam_send_ccb(dev, ccb) < 0) { ! cam_freeccb(ccb); ! retval = -1; ! goto bailout; ! } ! ! if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ! if (verbosemode != 0) ! cam_error_print(dev, ccb, CAM_ESF_ALL, ! CAM_EPF_ALL, stderr); ! retval = -1; ! goto bailout; ! } ! ! for (i = 0; i < sup_pages.length; i++) { ! if (sup_pages.list[i] == page_id) { ! retval = 1; ! goto bailout; ! } ! } ! bailout: ! if (ccb != NULL) ! cam_freeccb(ccb); ! ! return (retval); ! } ! ! /* ! * devtype is filled in with the type of device. ! * Returns 0 for success, non-zero for failure. ! */ ! int ! get_device_type(struct cam_device *dev, int retry_count, int timeout, ! int verbosemode, camcontrol_devtype *devtype) { ! struct ccb_getdev cgd; ! int retval = 0; ! ! retval = get_cgd(dev, &cgd); ! if (retval != 0) ! goto bailout; ! switch (cgd.protocol) { case PROTO_SCSI: ! break; case PROTO_ATA: case PROTO_ATAPI: case PROTO_SATAPM: ! *devtype = CC_DT_ATA; ! goto bailout; ! break; /*NOTREACHED*/ default: ! *devtype = CC_DT_UNKNOWN; ! goto bailout; ! break; /*NOTREACHED*/ ! } ! ! /* ! * Check for the ATA Information VPD page (0x89). If this is an ! * ATA device behind a SCSI to ATA translation layer, this VPD page ! * should be present. ! * ! * If that VPD page isn't present, or we get an error back from the ! * INQUIRY command, we'll just treat it as a normal SCSI device. ! */ ! retval = dev_has_vpd_page(dev, SVPD_ATA_INFORMATION, retry_count, ! timeout, verbosemode); ! if (retval == 1) ! *devtype = CC_DT_ATA_BEHIND_SCSI; ! else ! *devtype = CC_DT_SCSI; ! ! retval = 0; ! ! bailout: ! return (retval); ! } ! ! void ! build_ata_cmd(union ccb *ccb, uint32_t retry_count, uint32_t flags, ! uint8_t tag_action, uint8_t protocol, uint8_t ata_flags, uint16_t features, ! uint16_t sector_count, uint64_t lba, uint8_t command, uint8_t *data_ptr, ! uint16_t dxfer_len, uint8_t sense_len, uint32_t timeout, ! int is48bit, camcontrol_devtype devtype) ! { ! if (devtype == CC_DT_ATA) { ! cam_fill_ataio(&ccb->ataio, ! /*retries*/ retry_count, ! /*cbfcnp*/ NULL, ! /*flags*/ flags, ! /*tag_action*/ tag_action, ! /*data_ptr*/ data_ptr, ! /*dxfer_len*/ dxfer_len, ! /*timeout*/ timeout); ! if (is48bit || lba > ATA_MAX_28BIT_LBA) ! ata_48bit_cmd(&ccb->ataio, command, features, lba, ! sector_count); ! else ! ata_28bit_cmd(&ccb->ataio, command, features, lba, ! sector_count); ! } else { ! if (is48bit || lba > ATA_MAX_28BIT_LBA) ! protocol |= AP_EXTEND; ! ! scsi_ata_pass_16(&ccb->csio, ! /*retries*/ retry_count, ! /*cbfcnp*/ NULL, ! /*flags*/ flags, ! /*tag_action*/ tag_action, ! /*protocol*/ protocol, ! /*ata_flags*/ ata_flags, ! /*features*/ features, ! /*sector_count*/ sector_count, ! /*lba*/ lba, ! /*command*/ command, ! /*control*/ 0, ! /*data_ptr*/ data_ptr, ! /*dxfer_len*/ dxfer_len, ! /*sense_len*/ sense_len, ! /*timeout*/ timeout); } } + static void cpi_print(struct ccb_pathinq *cpi) { *************** *** 8050,8055 **** --- 8206,8666 ---- return (retval); } + int + scsigetopcodes(struct cam_device *device, int opcode_set, int opcode, + int show_sa_errors, int sa_set, int service_action, + int timeout_desc, int retry_count, int timeout, int verbosemode, + uint32_t *fill_len, uint8_t **data_ptr) + { + union ccb *ccb = NULL; + uint8_t *buf = NULL; + uint32_t alloc_len = 0, num_opcodes; + uint32_t valid_len = 0; + uint32_t avail_len = 0; + struct scsi_report_supported_opcodes_all *all_hdr; + struct scsi_report_supported_opcodes_one *one; + int options = 0; + int retval = 0; + + /* + * Make it clear that we haven't yet allocated or filled anything. + */ + *fill_len = 0; + *data_ptr = NULL; + + ccb = cam_getccb(device); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + retval = 1; + goto bailout; + } + + /* 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)); + + if (opcode_set != 0) { + options |= RSO_OPTIONS_OC; + num_opcodes = 1; + alloc_len = sizeof(*one) + CAM_MAX_CDBLEN; + } else { + num_opcodes = 256; + alloc_len = sizeof(*all_hdr) + (num_opcodes * + sizeof(struct scsi_report_supported_opcodes_descr)); + } + + if (timeout_desc != 0) { + options |= RSO_RCTD; + alloc_len += num_opcodes * + sizeof(struct scsi_report_supported_opcodes_timeout); + } + + if (sa_set != 0) { + options |= RSO_OPTIONS_OC_SA; + if (show_sa_errors != 0) + options &= ~RSO_OPTIONS_OC; + } + + retry_alloc: + if (buf != NULL) { + free(buf); + buf = NULL; + } + + buf = malloc(alloc_len); + if (buf == NULL) { + warn("Unable to allocate %u bytes", alloc_len); + retval = 1; + goto bailout; + } + bzero(buf, alloc_len); + + scsi_report_supported_opcodes(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*options*/ options, + /*req_opcode*/ opcode, + /*req_service_action*/ service_action, + /*data_ptr*/ buf, + /*dxfer_len*/ alloc_len, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 10000); + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (retry_count != 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (cam_send_ccb(device, ccb) < 0) { + perror("error sending REPORT SUPPORTED OPERATION CODES"); + retval = 1; + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + if (verbosemode != 0) + cam_error_print(device, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + + retval = 1; + goto bailout; + } + + valid_len = ccb->csio.dxfer_len - ccb->csio.resid; + + if (((options & RSO_OPTIONS_MASK) == RSO_OPTIONS_ALL) + && (valid_len >= sizeof(*all_hdr))) { + all_hdr = (struct scsi_report_supported_opcodes_all *)buf; + avail_len = scsi_4btoul(all_hdr->length) + sizeof(*all_hdr); + } else if (((options & RSO_OPTIONS_MASK) != RSO_OPTIONS_ALL) + && (valid_len >= sizeof(*one))) { + uint32_t cdb_length; + + one = (struct scsi_report_supported_opcodes_one *)buf; + cdb_length = scsi_2btoul(one->cdb_length); + avail_len = sizeof(*one) + cdb_length; + if (one->support & RSO_ONE_CTDP) { + struct scsi_report_supported_opcodes_timeout *td; + + td = (struct scsi_report_supported_opcodes_timeout *) + &buf[avail_len]; + if (valid_len >= (avail_len + sizeof(td->length))) { + avail_len += scsi_2btoul(td->length) + + sizeof(td->length); + } else { + avail_len += sizeof(*td); + } + } + } + + /* + * avail_len could be zero if we didn't get enough data back from + * thet target to determine + */ + if ((avail_len != 0) + && (avail_len > valid_len)) { + alloc_len = avail_len; + goto retry_alloc; + } + + *fill_len = valid_len; + *data_ptr = buf; + bailout: + if (retval != 0) + free(buf); + + cam_freeccb(ccb); + + return (retval); + } + + static int + scsiprintoneopcode(struct cam_device *device, int req_opcode, int sa_set, + int req_sa, uint8_t *buf, uint32_t valid_len) + { + struct scsi_report_supported_opcodes_one *one; + struct scsi_report_supported_opcodes_timeout *td; + uint32_t cdb_len = 0, td_len = 0; + const char *op_desc = NULL; + unsigned int i; + int retval = 0; + + one = (struct scsi_report_supported_opcodes_one *)buf; + + /* + * If we don't have the full single opcode descriptor, no point in + * continuing. + */ + if (valid_len < __offsetof(struct scsi_report_supported_opcodes_one, + cdb_length)) { + warnx("Only %u bytes returned, not enough to verify support", + valid_len); + retval = 1; + goto bailout; + } + + op_desc = scsi_op_desc(req_opcode, &device->inq_data); + + printf("%s (0x%02x)", op_desc != NULL ? op_desc : "UNKNOWN", + req_opcode); + if (sa_set != 0) + printf(", SA 0x%x", req_sa); + printf(": "); + + switch (one->support & RSO_ONE_SUP_MASK) { + case RSO_ONE_SUP_UNAVAIL: + printf("No command support information currently available\n"); + break; + case RSO_ONE_SUP_NOT_SUP: + printf("Command not supported\n"); + retval = 1; + goto bailout; + break; /*NOTREACHED*/ + case RSO_ONE_SUP_AVAIL: + printf("Command is supported, complies with a SCSI standard\n"); + break; + case RSO_ONE_SUP_VENDOR: + printf("Command is supported, vendor-specific " + "implementation\n"); + break; + default: + printf("Unknown command support flags 0x%#x\n", + one->support & RSO_ONE_SUP_MASK); + break; + } + + /* + * If we don't have the CDB length, it isn't exactly an error, the + * command probably isn't supported. + */ + if (valid_len < __offsetof(struct scsi_report_supported_opcodes_one, + cdb_usage)) + goto bailout; + + cdb_len = scsi_2btoul(one->cdb_length); + + /* + * If our valid data doesn't include the full reported length, + * return. The caller should have detected this and adjusted his + * allocation length to get all of the available data. + */ + if (valid_len < sizeof(*one) + cdb_len) { + retval = 1; + goto bailout; + } + + /* + * If all we have is the opcode, there is no point in printing out + * the usage bitmap. + */ + if (cdb_len <= 1) { + retval = 1; + goto bailout; + } + + printf("CDB usage bitmap:"); + for (i = 0; i < cdb_len; i++) { + printf(" %02x", one->cdb_usage[i]); + } + printf("\n"); + + /* + * If we don't have a timeout descriptor, we're done. + */ + if ((one->support & RSO_ONE_CTDP) == 0) + goto bailout; + + /* + * If we don't have enough valid length to include the timeout + * descriptor length, we're done. + */ + if (valid_len < (sizeof(*one) + cdb_len + sizeof(td->length))) + goto bailout; + + td = (struct scsi_report_supported_opcodes_timeout *) + &buf[sizeof(*one) + cdb_len]; + td_len = scsi_2btoul(td->length); + td_len += sizeof(td->length); + + /* + * If we don't have the full timeout descriptor, we're done. + */ + if (td_len < sizeof(*td)) + goto bailout; + + /* + * If we don't have enough valid length to contain the full timeout + * descriptor, we're done. + */ + if (valid_len < (sizeof(*one) + cdb_len + td_len)) + goto bailout; + + printf("Timeout information:\n"); + printf("Command-specific: 0x%02x\n", td->cmd_specific); + printf("Nominal timeout: %u seconds\n", + scsi_4btoul(td->nominal_time)); + printf("Recommended timeout: %u seconds\n", + scsi_4btoul(td->recommended_time)); + + bailout: + return (retval); + } + + static int + scsiprintopcodes(struct cam_device *device, int td_req, uint8_t *buf, + uint32_t valid_len) + { + struct scsi_report_supported_opcodes_all *hdr; + struct scsi_report_supported_opcodes_descr *desc; + uint32_t avail_len = 0, used_len = 0; + uint8_t *cur_ptr; + int retval = 0; + + if (valid_len < sizeof(*hdr)) { + warnx("%s: not enough returned data (%u bytes) opcode list", + __func__, valid_len); + retval = 1; + goto bailout; + } + hdr = (struct scsi_report_supported_opcodes_all *)buf; + avail_len = scsi_4btoul(hdr->length); + avail_len += sizeof(hdr->length); + /* + * Take the lesser of the amount of data the drive claims is + * available, and the amount of data the HBA says was returned. + */ + avail_len = MIN(avail_len, valid_len); + + used_len = sizeof(hdr->length); + + printf("%-6s %4s %8s ", + "Opcode", "SA", "CDB len" ); + + if (td_req != 0) + printf("%5s %6s %6s ", "CS", "Nom", "Rec"); + printf(" Description\n"); + + while ((avail_len - used_len) > sizeof(*desc)) { + struct scsi_report_supported_opcodes_timeout *td; + uint32_t td_len; + const char *op_desc = NULL; + + cur_ptr = &buf[used_len]; + desc = (struct scsi_report_supported_opcodes_descr *)cur_ptr; + + op_desc = scsi_op_desc(desc->opcode, &device->inq_data); + if (op_desc == NULL) + op_desc = "UNKNOWN"; + + printf("0x%02x %#4x %8u ", desc->opcode, + scsi_2btoul(desc->service_action), + scsi_2btoul(desc->cdb_length)); + + used_len += sizeof(*desc); + + if ((desc->flags & RSO_CTDP) == 0) { + printf(" %s\n", op_desc); + continue; + } + + /* + * If we don't have enough space to fit a timeout + * descriptor, then we're done. + */ + if (avail_len - used_len < sizeof(*td)) { + used_len = avail_len; + printf(" %s\n", op_desc); + continue; + } + cur_ptr = &buf[used_len]; + td = (struct scsi_report_supported_opcodes_timeout *)cur_ptr; + td_len = scsi_2btoul(td->length); + td_len += sizeof(td->length); + + used_len += td_len; + /* + * If the given timeout descriptor length is less than what + * we understand, skip it. + */ + if (td_len < sizeof(*td)) { + printf(" %s\n", op_desc); + continue; + } + + printf(" 0x%02x %6u %6u %s\n", td->cmd_specific, + scsi_4btoul(td->nominal_time), + scsi_4btoul(td->recommended_time), op_desc); + } + bailout: + return (retval); + } + + static int + scsiopcodes(struct cam_device *device, int argc, char **argv, + char *combinedopt, int retry_count, int timeout, int verbosemode) + { + int c; + uint32_t opcode = 0, service_action = 0; + int td_set = 0, opcode_set = 0, sa_set = 0; + int show_sa_errors = 1; + uint32_t valid_len = 0; + uint8_t *buf = NULL; + char *endptr; + int retval = 0; + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'N': + show_sa_errors = 0; + break; + case 'o': + opcode = strtoul(optarg, &endptr, 0); + if (*endptr != '\0') { + warnx("Invalid opcode \"%s\", must be a number", + optarg); + retval = 1; + goto bailout; + } + if (opcode > 0xff) { + warnx("Invalid opcode 0x%#x, must be between" + "0 and 0xff inclusive", opcode); + retval = 1; + goto bailout; + } + opcode_set = 1; + break; + case 's': + service_action = strtoul(optarg, &endptr, 0); + if (*endptr != '\0') { + warnx("Invalid service action \"%s\", must " + "be a number", optarg); + retval = 1; + goto bailout; + } + if (service_action > 0xffff) { + warnx("Invalid service action 0x%#x, must " + "be between 0 and 0xffff inclusive", + service_action); + retval = 1; + } + sa_set = 1; + break; + case 'T': + td_set = 1; + break; + default: + break; + } + } + + if ((sa_set != 0) + && (opcode_set == 0)) { + warnx("You must specify an opcode with -o if a service " + "action is given"); + retval = 1; + goto bailout; + } + retval = scsigetopcodes(device, opcode_set, opcode, show_sa_errors, + sa_set, service_action, td_set, retry_count, + timeout, verbosemode, &valid_len, &buf); + if (retval != 0) + goto bailout; + + if ((opcode_set != 0) + || (sa_set != 0)) { + retval = scsiprintoneopcode(device, opcode, sa_set, + service_action, buf, valid_len); + } else { + retval = scsiprintopcodes(device, td_set, buf, valid_len); + } + + bailout: + free(buf); + + return (retval); + } + #endif /* MINIMALISTIC */ void *************** *** 8109,8115 **** " camcontrol sleep [dev_id][generic args]\n" " camcontrol apm [dev_id][generic args][-l level]\n" " camcontrol aam [dev_id][generic args][-l level]\n" ! " camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-y][-s]\n" " camcontrol security [dev_id][generic args]\n" " <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n" " [-l ] [-q] [-s pwd] [-T timeout]\n" --- 8720,8727 ---- " camcontrol sleep [dev_id][generic args]\n" " camcontrol apm [dev_id][generic args][-l level]\n" " camcontrol aam [dev_id][generic args][-l level]\n" ! " camcontrol fwdownload [dev_id][generic args] <-f fw_image> [-q]\n" ! " [-s][-y]\n" " camcontrol security [dev_id][generic args]\n" " <-d pwd | -e pwd | -f | -h pwd | -k pwd>\n" " [-l ] [-q] [-s pwd] [-T timeout]\n" *************** *** 8122,8127 **** --- 8734,8741 ---- " camcontrol attrib [dev_id][generic args] <-r action|-w attr>\n" " [-a attr_num][-c][-e elem][-F form1,form1]\n" " [-p part][-s start][-T type][-V vol]\n" + " camcontrol opcodes [dev_id][generic args][-o opcode][-s SA]\n" + " [-N][-T]\n" #endif /* MINIMALISTIC */ " camcontrol help\n"); if (!printlong) *************** *** 8162,8167 **** --- 8776,8782 ---- "security report or send ATA security commands to the named device\n" "persist send the SCSI PERSISTENT RESERVE IN or OUT commands\n" "attrib send the SCSI READ or WRITE ATTRIBUTE commands\n" + "opcodes send the SCSI REPORT SUPPORTED OPCODES command\n" "help this message\n" "Device Identifiers:\n" "bus:target specify the bus and target, lun defaults to 0\n" *************** *** 8266,8274 **** "-t number of seconds before respective state.\n" "fwdownload arguments:\n" "-f fw_image path to firmware image file\n" ! "-y don't ask any questions\n" "-s run in simulation mode\n" "-v print info for every firmware segment sent to device\n" "security arguments:\n" "-d pwd disable security using the given password for the selected\n" " user\n" --- 8881,8890 ---- "-t number of seconds before respective state.\n" "fwdownload arguments:\n" "-f fw_image path to firmware image file\n" ! "-q don't print informational messages, only errors\n" "-s run in simulation mode\n" "-v print info for every firmware segment sent to device\n" + "-y don't ask any questions\n" "security arguments:\n" "-d pwd disable security using the given password for the selected\n" " user\n" *************** *** 8326,8331 **** --- 8942,8952 ---- "-s start_attr request attributes starting at the given number\n" "-T elem_type specify the element type (used with -e)\n" "-V logical_vol specify the logical volume ID\n" + "opcodes arguments:\n" + "-o opcode specify the individual opcode to list\n" + "-s service_action specify the service action for the opcode\n" + "-N do not return SCSI error for unsupported SA\n" + "-T request nominal and recommended timeout values\n" ); #endif /* MINIMALISTIC */ } *************** *** 8658,8665 **** break; case CAM_CMD_DOWNLOAD_FW: error = fwdownload(cam_dev, argc, argv, combinedopt, ! arglist & CAM_ARG_VERBOSE, retry_count, timeout, ! get_disk_type(cam_dev)); break; case CAM_CMD_SANITIZE: error = scsisanitize(cam_dev, argc, argv, --- 9279,9285 ---- break; case CAM_CMD_DOWNLOAD_FW: error = fwdownload(cam_dev, argc, argv, combinedopt, ! arglist & CAM_ARG_VERBOSE, retry_count, timeout); break; case CAM_CMD_SANITIZE: error = scsisanitize(cam_dev, argc, argv, *************** *** 8675,8680 **** --- 9295,9304 ---- retry_count, timeout, arglist & CAM_ARG_VERBOSE, arglist & CAM_ARG_ERR_RECOVER); break; + case CAM_CMD_OPCODES: + error = scsiopcodes(cam_dev, argc, argv, combinedopt, + retry_count, timeout, arglist & CAM_ARG_VERBOSE); + break; #endif /* MINIMALISTIC */ case CAM_CMD_USAGE: usage(1); *** src/sbin/camcontrol/camcontrol.h.orig --- src/sbin/camcontrol/camcontrol.h *************** *** 37,42 **** --- 37,50 ---- CC_OR_FOUND } camcontrol_optret; + typedef enum { + CC_DT_NONE, + CC_DT_SCSI, + CC_DT_ATA_BEHIND_SCSI, + CC_DT_ATA, + CC_DT_UNKNOWN + } camcontrol_devtype; + /* * get_hook: Structure for evaluating args in a callback. */ *************** *** 49,57 **** extern int verbose; int fwdownload(struct cam_device *device, int argc, char **argv, ! char *combinedopt, int printerrors, int retry_count, int timeout, ! const char */*type*/); 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); --- 57,78 ---- extern int verbose; + int ata_do_identify(struct cam_device *device, int retry_count, int timeout, + union ccb *ccb, struct ata_params **ident_bufp); + int dev_has_vpd_page(struct cam_device *dev, uint8_t page_id, int retry_count, + int timeout, int verbosemode); + int get_device_type(struct cam_device *dev, int retry_count, int timeout, + int verbosemode, camcontrol_devtype *devtype); + void build_ata_cmd(union ccb *ccb, uint32_t retry_count, uint32_t flags, + uint8_t tag_action, uint8_t protocol, uint8_t ata_flags, + uint16_t features, uint16_t sector_count, uint64_t lba, + uint8_t command, uint8_t *data_ptr, uint16_t dxfer_len, + uint8_t sense_len, uint32_t timeout, int is48bit, + camcontrol_devtype devtype); + int camxferrate(struct cam_device *device); int fwdownload(struct cam_device *device, int argc, char **argv, ! char *combinedopt, int printerrors, int retry_count, ! int timeout); 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); *************** *** 63,68 **** --- 84,93 ---- int retry_count, int timeout); int scsidoinquiry(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout); + int scsigetopcodes(struct cam_device *device, int opcode_set, int opcode, + int show_sa_errors, int sa_set, int service_action, + int timeout_desc, int retry_count, int timeout, + int verbosemode, uint32_t *fill_len, uint8_t **data_ptr); int scsipersist(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout, int verbose, int err_recover); *** src/sbin/camcontrol/fwdownload.c.orig --- src/sbin/camcontrol/fwdownload.c *************** *** 68,76 **** #include "camcontrol.h" ! #define CMD_TIMEOUT 50000 /* 50 seconds */ typedef enum { VENDOR_HITACHI, VENDOR_HP, VENDOR_IBM, --- 68,77 ---- #include "camcontrol.h" ! #define WB_TIMEOUT 50000 /* 50 seconds */ typedef enum { + VENDOR_HGST, VENDOR_HITACHI, VENDOR_HP, VENDOR_IBM, *************** *** 79,110 **** VENDOR_QUANTUM, VENDOR_SAMSUNG, VENDOR_SEAGATE, VENDOR_UNKNOWN } fw_vendor_t; struct fw_vendor { fw_vendor_t type; const char *pattern; int max_pkt_size; u_int8_t cdb_byte2; u_int8_t cdb_byte2_last; int inc_cdb_buffer_id; int inc_cdb_offset; }; ! static const struct fw_vendor vendors_list[] = { ! {VENDOR_HITACHI, "HITACHI", 0x8000, 0x05, 0x05, 1, 0}, ! {VENDOR_HP, "HP", 0x8000, 0x07, 0x07, 0, 1}, ! {VENDOR_IBM, "IBM", 0x8000, 0x05, 0x05, 1, 0}, ! {VENDOR_PLEXTOR, "PLEXTOR", 0x2000, 0x04, 0x05, 0, 1}, ! {VENDOR_QUALSTAR, "QUALSTAR", 0x2030, 0x05, 0x05, 0, 0}, ! {VENDOR_QUANTUM, "QUANTUM", 0x2000, 0x04, 0x05, 0, 1}, ! {VENDOR_SAMSUNG, "SAMSUNG", 0x8000, 0x07, 0x07, 0, 1}, ! {VENDOR_SEAGATE, "SEAGATE", 0x8000, 0x07, 0x07, 0, 1}, ! /* the next 2 are SATA disks going through SAS HBA */ ! {VENDOR_SEAGATE, "ATA ST", 0x8000, 0x07, 0x07, 0, 1}, ! {VENDOR_HITACHI, "ATA HDS", 0x8000, 0x05, 0x05, 1, 0}, ! {VENDOR_UNKNOWN, NULL, 0x0000, 0x00, 0x00, 0, 0} }; #ifndef ATA_DOWNLOAD_MICROCODE --- 80,242 ---- VENDOR_QUANTUM, VENDOR_SAMSUNG, VENDOR_SEAGATE, + VENDOR_SMART, + VENDOR_ATA, VENDOR_UNKNOWN } fw_vendor_t; + /* + * FW_TUR_READY: The drive must return good status for a test unit ready. + * + * FW_TUR_NOT_READY: The drive must return not ready status for a test unit + * ready. You may want this in a removable media drive. + * + * FW_TUR_NA: It doesn't matter whether the drive is ready or not. + * This may be the case for a removable media drive. + */ + typedef enum { + FW_TUR_NONE, + FW_TUR_READY, + FW_TUR_NOT_READY, + FW_TUR_NA + } fw_tur_status; + + /* + * FW_TIMEOUT_DEFAULT: Attempt to probe for a WRITE BUFFER timeout + * value from the drive. If we get an answer, + * use the Recommended timeout. Otherwise, + * use the default value from the table. + * + * FW_TIMEOUT_DEV_REPORTED: The timeout value was probed directly from + * the device. + * + * FW_TIMEOUT_NO_PROBE: Do not ask the device for a WRITE BUFFER + * timeout value. Use the device-specific + * value. + * + * FW_TIMEOUT_USER_SPEC: The user specified a timeout on the command + * line with the -t option. This overrides any + * probe or default timeout. + */ + typedef enum { + FW_TIMEOUT_DEFAULT, + FW_TIMEOUT_DEV_REPORTED, + FW_TIMEOUT_NO_PROBE, + FW_TIMEOUT_USER_SPEC + } fw_timeout_type; + + /* + * type: Enumeration for the particular vendor. + * + * pattern: Pattern to match for the Vendor ID from the SCSI + * Inquiry data. + * + * dev_type: SCSI device type to match, or T_ANY to match any + * device from the given vendor. Note that if there + * is a specific device type listed for a particular + * vendor, it must be listed before a T_ANY entry. + * + * max_pkt_size: Maximum packet size when talking to a device. Note + * that although large data sizes may be supported by + * the target device, they may not be supported by the + * OS or the controller. + * + * cdb_byte2: This specifies byte 2 (byte 1 when counting from 0) + * of the CDB. This is generally the WRITE BUFFER mode. + * + * cdb_byte2_last: This specifies byte 2 for the last chunk of the + * download. + * + * inc_cdb_buffer_id: Increment the buffer ID by 1 for each chunk sent + * down to the drive. + * + * inc_cdb_offset: Increment the offset field in the CDB with the byte + * offset into the firmware file. + * + * tur_status: Pay attention to whether the device is ready before + * upgrading the firmware, or not. See above for the + * values. + */ struct fw_vendor { fw_vendor_t type; const char *pattern; + int dev_type; int max_pkt_size; u_int8_t cdb_byte2; u_int8_t cdb_byte2_last; int inc_cdb_buffer_id; int inc_cdb_offset; + fw_tur_status tur_status; + int timeout_ms; + fw_timeout_type timeout_type; }; ! /* ! * Vendor notes: ! * ! * HGST: The packets need to be sent in multiples of 4K. ! * ! * IBM: For LTO and TS drives, the buffer ID is ignored in mode 7 (and ! * some other modes). It treats the request as a firmware download. ! * The offset (and therefore the length of each chunk sent) needs ! * to be a multiple of the offset boundary specified for firmware ! * (buffer ID 4) in the read buffer command. At least for LTO-6, ! * that seems to be 0, but using a 32K chunk size should satisfy ! * most any alignment requirement. ! * ! * SmrtStor: Mode 5 is also supported, but since the firmware is 400KB or ! * so, we can't fit it in a single request in most cases. ! */ ! static struct fw_vendor vendors_list[] = { ! {VENDOR_HGST, "HGST", T_DIRECT, ! 0x1000, 0x07, 0x07, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_HITACHI, "HITACHI", T_ANY, ! 0x8000, 0x05, 0x05, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_HP, "HP", T_ANY, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_IBM, "IBM", T_SEQUENTIAL, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_NA, 300 * 1000, FW_TIMEOUT_DEFAULT}, ! {VENDOR_IBM, "IBM", T_ANY, ! 0x8000, 0x05, 0x05, 1, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_PLEXTOR, "PLEXTOR", T_ANY, ! 0x2000, 0x04, 0x05, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_QUALSTAR, "QUALSTAR", T_ANY, ! 0x2030, 0x05, 0x05, 0, 0, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_QUANTUM, "QUANTUM", T_ANY, ! 0x2000, 0x04, 0x05, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_SAMSUNG, "SAMSUNG", T_ANY, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_SEAGATE, "SEAGATE", T_ANY, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! {VENDOR_SMART, "SmrtStor", T_DIRECT, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, FW_TIMEOUT_DEFAULT}, ! ! /* ! * We match any ATA device. This is really just a placeholder, ! * since we won't actually send a WRITE BUFFER with any of the ! * listed parameters. If a SATA device is behind a SAS controller, ! * the SCSI to ATA translation code (at least for LSI) doesn't ! * generaly translate a SCSI WRITE BUFFER into an ATA DOWNLOAD ! * MICROCODE command. So, we use the SCSI ATA PASS_THROUGH command ! * to send the ATA DOWNLOAD MICROCODE command instead. ! */ ! {VENDOR_ATA, "ATA", T_ANY, ! 0x8000, 0x07, 0x07, 0, 1, FW_TUR_READY, WB_TIMEOUT, ! FW_TIMEOUT_NO_PROBE}, ! {VENDOR_UNKNOWN, NULL, T_ANY, ! 0x0000, 0x00, 0x00, 0, 0, FW_TUR_NONE, WB_TIMEOUT, FW_TIMEOUT_DEFAULT} ! }; ! ! struct fw_timeout_desc { ! fw_timeout_type timeout_type; ! const char *timeout_desc; ! }; ! ! static const struct fw_timeout_desc fw_timeout_desc_table[] = { ! { FW_TIMEOUT_DEFAULT, "the default" }, ! { FW_TIMEOUT_DEV_REPORTED, "recommended by this particular device" }, ! { FW_TIMEOUT_NO_PROBE, "the default" }, ! { FW_TIMEOUT_USER_SPEC, "what was specified on the command line" } }; #ifndef ATA_DOWNLOAD_MICROCODE *************** *** 128,170 **** #define UNKNOWN_MAX_PKT_SIZE 0x8000 #endif ! static const struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev); ! static char *fw_read_img(const char *fw_img_path, ! const struct fw_vendor *vp, int *num_bytes); ! static int fw_download_img(struct cam_device *cam_dev, ! const struct fw_vendor *vp, char *buf, int img_size, ! int sim_mode, int printerrors, int retry_count, int timeout, ! const char */*name*/, const char */*type*/); /* * Find entry in vendors list that belongs to * the vendor of given cam device. */ ! static const struct fw_vendor * ! fw_get_vendor(struct cam_device *cam_dev) { ! char vendor[SID_VENDOR_SIZE + 1]; ! const struct fw_vendor *vp; if (cam_dev == NULL) return (NULL); ! cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor, ! sizeof(cam_dev->inq_data.vendor), sizeof(vendor)); for (vp = vendors_list; vp->pattern != NULL; vp++) { if (!cam_strmatch((const u_char *)vendor, ! (const u_char *)vp->pattern, strlen(vendor))) ! break; } return (vp); } /* * Allocate a buffer and read fw image file into it * from given path. Number of bytes read is stored * in num_bytes. */ static char * ! fw_read_img(const char *fw_img_path, const struct fw_vendor *vp, int *num_bytes) { int fd; struct stat stbuf; --- 260,567 ---- #define UNKNOWN_MAX_PKT_SIZE 0x8000 #endif ! static struct fw_vendor *fw_get_vendor(struct cam_device *cam_dev, ! struct ata_params *ident_buf); ! static int fw_get_timeout(struct cam_device *cam_dev, struct fw_vendor *vp, ! int retry_count, int timeout); ! static int fw_validate_ibm(struct cam_device *dev, int retry_count, ! int timeout, int fd, char *buf, ! const char *fw_img_path, int quiet); ! static char *fw_read_img(struct cam_device *dev, int retry_count, ! int timeout, int quiet, const char *fw_img_path, ! struct fw_vendor *vp, int *num_bytes); ! static int fw_check_device_ready(struct cam_device *dev, ! camcontrol_devtype devtype, ! struct fw_vendor *vp, int printerrors, ! int timeout); ! static int fw_download_img(struct cam_device *cam_dev, ! struct fw_vendor *vp, char *buf, int img_size, ! int sim_mode, int printerrors, int quiet, ! int retry_count, int timeout, const char */*name*/, ! camcontrol_devtype devtype); /* * Find entry in vendors list that belongs to * the vendor of given cam device. */ ! static struct fw_vendor * ! fw_get_vendor(struct cam_device *cam_dev, struct ata_params *ident_buf) { ! char vendor[42]; ! struct fw_vendor *vp; if (cam_dev == NULL) return (NULL); ! ! if (ident_buf != NULL) { ! cam_strvis((u_char *)vendor, ident_buf->model, ! sizeof(ident_buf->model), sizeof(vendor)); ! for (vp = vendors_list; vp->pattern != NULL; vp++) { ! if (vp->type == VENDOR_ATA) ! return (vp); ! } ! } else { ! cam_strvis((u_char *)vendor, (u_char *)cam_dev->inq_data.vendor, ! sizeof(cam_dev->inq_data.vendor), sizeof(vendor)); ! } for (vp = vendors_list; vp->pattern != NULL; vp++) { if (!cam_strmatch((const u_char *)vendor, ! (const u_char *)vp->pattern, strlen(vendor))) { ! if ((vp->dev_type == T_ANY) ! || (vp->dev_type == SID_TYPE(&cam_dev->inq_data))) ! break; ! } } return (vp); } + static int + fw_get_timeout(struct cam_device *cam_dev, struct fw_vendor *vp, + int retry_count, int timeout) + { + struct scsi_report_supported_opcodes_one *one; + struct scsi_report_supported_opcodes_timeout *td; + uint8_t *buf = NULL; + uint32_t fill_len = 0, cdb_len = 0, rec_timeout = 0; + int retval = 0; + + /* + * If the user has specified a timeout on the command line, we let + * him override any default or probed value. + */ + if (timeout != 0) { + vp->timeout_type = FW_TIMEOUT_USER_SPEC; + vp->timeout_ms = timeout; + goto bailout; + } + + /* + * Check to see whether we should probe for a timeout for this + * device. + */ + if (vp->timeout_type == FW_TIMEOUT_NO_PROBE) + goto bailout; + + retval = scsigetopcodes(/*device*/ cam_dev, + /*opcode_set*/ 1, + /*opcode*/ WRITE_BUFFER, + /*show_sa_errors*/ 1, + /*sa_set*/ 0, + /*service_action*/ 0, + /*timeout_desc*/ 1, + /*retry_count*/ retry_count, + /*timeout*/ 10000, + /*verbose*/ 0, + /*fill_len*/ &fill_len, + /*data_ptr*/ &buf); + /* + * It isn't an error if we can't get a timeout descriptor. We just + * continue on with the default timeout. + */ + if (retval != 0) { + retval = 0; + goto bailout; + } + + /* + * Even if the drive didn't return a SCSI error, if we don't have + * enough data to contain the one opcode descriptor, the CDB + * structure and a timeout descriptor, we don't have the timeout + * value we're looking for. So we'll just fall back to the + * default value. + */ + if (fill_len < (sizeof(*one) + sizeof(struct scsi_write_buffer) + + sizeof(*td))) + goto bailout; + + one = (struct scsi_report_supported_opcodes_one *)buf; + + /* + * If the drive claims to not support the WRITE BUFFER command... + * fall back to the default timeout value and let things fail on + * the actual firmware download. + */ + if ((one->support & RSO_ONE_SUP_MASK) == RSO_ONE_SUP_NOT_SUP) + goto bailout; + + cdb_len = scsi_2btoul(one->cdb_length); + td = (struct scsi_report_supported_opcodes_timeout *) + &buf[sizeof(*one) + cdb_len]; + + rec_timeout = scsi_4btoul(td->recommended_time); + /* + * If the recommended timeout is 0, then the device has probably + * returned a bogus value. + */ + if (rec_timeout == 0) + goto bailout; + + /* CAM timeouts are in ms */ + rec_timeout *= 1000; + + vp->timeout_ms = rec_timeout; + vp->timeout_type = FW_TIMEOUT_DEV_REPORTED; + + bailout: + return (retval); + } + + #define SVPD_IBM_FW_DESIGNATION 0x03 + + /* + * IBM LTO and TS tape drives have an INQUIRY VPD page 0x3 with the following + * format: + */ + struct fw_ibm_tape_fw_designation { + uint8_t device; + uint8_t page_code; + uint8_t reserved; + uint8_t length; + uint8_t ascii_length; + uint8_t reserved2[3]; + uint8_t load_id[4]; + uint8_t fw_rev[4]; + uint8_t ptf_number[4]; + uint8_t patch_number[4]; + uint8_t ru_name[8]; + uint8_t lib_seq_num[5]; + }; + + /* + * The firmware for IBM tape drives has the following header format. The + * load_id and ru_name in the header file should match what is returned in + * VPD page 0x3. + */ + struct fw_ibm_tape_fw_header { + uint8_t unspec[4]; + uint8_t length[4]; /* Firmware and header! */ + uint8_t load_id[4]; + uint8_t fw_rev[4]; + uint8_t reserved[8]; + uint8_t ru_name[8]; + }; + + static int + fw_validate_ibm(struct cam_device *dev, int retry_count, int timeout, int fd, + char *buf, const char *fw_img_path, int quiet) + { + union ccb *ccb; + struct fw_ibm_tape_fw_designation vpd_page; + struct fw_ibm_tape_fw_header *header; + char drive_rev[sizeof(vpd_page.fw_rev) + 1]; + char file_rev[sizeof(vpd_page.fw_rev) + 1]; + int retval = 1; + + ccb = cam_getccb(dev); + if (ccb == NULL) { + warnx("couldn't allocate CCB"); + goto bailout; + } + + /* 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)); + + bzero(&vpd_page, sizeof(vpd_page)); + + scsi_inquiry(&ccb->csio, + /*retries*/ retry_count, + /*cbfcnp*/ NULL, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /* inq_buf */ (u_int8_t *)&vpd_page, + /* inq_len */ sizeof(vpd_page), + /* evpd */ 1, + /* page_code */ SVPD_IBM_FW_DESIGNATION, + /* sense_len */ SSD_FULL_SIZE, + /* timeout */ timeout ? timeout : 5000); + + /* Disable freezing the device queue */ + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + if (retry_count != 0) + ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; + + if (cam_send_ccb(dev, ccb) < 0) { + warn("error getting firmware designation page"); + + cam_error_print(dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + + cam_freeccb(ccb); + goto bailout; + } + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + cam_error_print(dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + goto bailout; + } + + /* + * Read the firmware header only. + */ + if (read(fd, buf, sizeof(*header)) != sizeof(*header)) { + warn("unable to read %zu bytes from %s", sizeof(*header), + fw_img_path); + goto bailout; + } + + /* Rewind the file back to 0 for the full file read. */ + if (lseek(fd, 0, SEEK_SET) == -1) { + warn("Unable to lseek"); + goto bailout; + } + + header = (struct fw_ibm_tape_fw_header *)buf; + + bzero(drive_rev, sizeof(drive_rev)); + bcopy(vpd_page.fw_rev, drive_rev, sizeof(vpd_page.fw_rev)); + bzero(file_rev, sizeof(file_rev)); + bcopy(header->fw_rev, file_rev, sizeof(header->fw_rev)); + + if (quiet == 0) { + fprintf(stdout, "Current Drive Firmware version: %s\n", + drive_rev); + fprintf(stdout, "Firmware File version: %s\n", file_rev); + } + + /* + * For IBM tape drives the load ID and RU name reported by the + * drive should match what is in the firmware file. + */ + if (bcmp(vpd_page.load_id, header->load_id, + MIN(sizeof(vpd_page.load_id), sizeof(header->load_id))) != 0) { + warnx("Drive Firmware load ID 0x%x does not match firmware " + "file load ID 0x%x", scsi_4btoul(vpd_page.load_id), + scsi_4btoul(header->load_id)); + goto bailout; + } + + if (bcmp(vpd_page.ru_name, header->ru_name, + MIN(sizeof(vpd_page.ru_name), sizeof(header->ru_name))) != 0) { + warnx("Drive Firmware RU name 0x%jx does not match firmware " + "file RU name 0x%jx", + (uintmax_t)scsi_8btou64(vpd_page.ru_name), + (uintmax_t)scsi_8btou64(header->ru_name)); + goto bailout; + } + if (quiet == 0) + fprintf(stdout, "Firmware file is valid for this drive.\n"); + retval = 0; + bailout: + cam_freeccb(ccb); + + return (retval); + } + /* * Allocate a buffer and read fw image file into it * from given path. Number of bytes read is stored * in num_bytes. */ static char * ! fw_read_img(struct cam_device *dev, int retry_count, int timeout, int quiet, ! const char *fw_img_path, struct fw_vendor *vp, int *num_bytes) { int fd; struct stat stbuf; *************** *** 207,212 **** --- 604,617 ---- case VENDOR_QUALSTAR: skip_bytes = img_size % 1030; break; + case VENDOR_IBM: { + if (vp->dev_type != T_SEQUENTIAL) + break; + if (fw_validate_ibm(dev, retry_count, timeout, fd, buf, + fw_img_path, quiet) != 0) + goto bailout; + break; + } default: break; } *************** *** 234,318 **** return (NULL); } /* * Download firmware stored in buf to cam_dev. If simulation mode * is enabled, only show what packet sizes would be sent to the * device but do not sent any actual packets */ static int ! fw_download_img(struct cam_device *cam_dev, const struct fw_vendor *vp, ! char *buf, int img_size, int sim_mode, int printerrors, int retry_count, ! int timeout, const char *imgname, const char *type) { struct scsi_write_buffer cdb; progress_t progress; ! int size; ! union ccb *ccb; int pkt_count = 0; int max_pkt_size; u_int32_t pkt_size = 0; char *pkt_ptr = buf; u_int32_t offset; int last_pkt = 0; ! int16_t *ptr; if ((ccb = cam_getccb(cam_dev)) == NULL) { warnx("Could not allocate CCB"); ! return (1); } - if (strcmp(type, "scsi") == 0) { - scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, - SSD_FULL_SIZE, 5000); - } else if (strcmp(type, "ata") == 0) { - /* cam_getccb cleans up the header, caller has to zero the payload */ - bzero(&(&ccb->ccb_h)[1], - sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr)); ! ptr = (uint16_t *)malloc(sizeof(struct ata_params)); - if (ptr == NULL) { - cam_freeccb(ccb); - warnx("can't malloc memory for identify\n"); - return(1); - } - bzero(ptr, sizeof(struct ata_params)); - cam_fill_ataio(&ccb->ataio, - 1, - NULL, - /*flags*/CAM_DIR_IN, - MSG_SIMPLE_Q_TAG, - /*data_ptr*/(uint8_t *)ptr, - /*dxfer_len*/sizeof(struct ata_params), - timeout ? timeout : 30 * 1000); - ata_28bit_cmd(&ccb->ataio, ATA_ATA_IDENTIFY, 0, 0, 0); - } else { - warnx("weird disk type '%s'", type); - cam_freeccb(ccb); - return 1; - } - /* Disable freezing the device queue. */ - ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; - if (cam_send_ccb(cam_dev, ccb) < 0) { - warnx("Error sending identify/test unit ready"); - if (printerrors) - cam_error_print(cam_dev, ccb, CAM_ESF_ALL, - CAM_EPF_ALL, stderr); - cam_freeccb(ccb); - return(1); - } - if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { - warnx("Device is not ready"); - if (printerrors) - cam_error_print(cam_dev, ccb, CAM_ESF_ALL, - CAM_EPF_ALL, stderr); - cam_freeccb(ccb); - return (1); - } max_pkt_size = vp->max_pkt_size; ! if (vp->max_pkt_size == 0 && strcmp(type, "ata") == 0) { max_pkt_size = UNKNOWN_MAX_PKT_SIZE; ! } ! pkt_size = vp->max_pkt_size; progress_init(&progress, imgname, size = img_size); /* Download single fw packets. */ do { --- 639,800 ---- return (NULL); } + /* + * Returns 0 for "success", where success means that the device has met the + * requirement in the vendor structure for being ready or not ready when + * firmware is downloaded. + * + * Returns 1 for a failure to be ready to accept a firmware download. + * (e.g., a drive needs to be ready, but returns not ready) + * + * Returns -1 for any other failure. + */ + static int + fw_check_device_ready(struct cam_device *dev, camcontrol_devtype devtype, + struct fw_vendor *vp, int printerrors, int timeout) + { + union ccb *ccb; + int retval = 0; + int16_t *ptr = NULL; + size_t dxfer_len = 0; + + if ((ccb = cam_getccb(dev)) == NULL) { + warnx("Could not allocate CCB"); + retval = -1; + goto bailout; + } + + bzero(&(&ccb->ccb_h)[1], + sizeof(union ccb) - sizeof(struct ccb_hdr)); + + if (devtype != CC_DT_SCSI) { + dxfer_len = sizeof(struct ata_params); + + ptr = (uint16_t *)malloc(dxfer_len); + if (ptr == NULL) { + warnx("can't malloc memory for identify"); + retval = -1; + goto bailout; + } + bzero(ptr, dxfer_len); + } + + switch (devtype) { + case CC_DT_SCSI: + scsi_test_unit_ready(&ccb->csio, + /*retries*/ 0, + /*cbfcnp*/ NULL, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ 5000); + break; + case CC_DT_ATA_BEHIND_SCSI: + case CC_DT_ATA: { + build_ata_cmd(ccb, + /*retries*/ 1, + /*flags*/ CAM_DIR_IN, + /*tag_action*/ MSG_SIMPLE_Q_TAG, + /*protocol*/ AP_PROTO_PIO_IN, + /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES | + AP_FLAG_TLEN_SECT_CNT | + AP_FLAG_TDIR_FROM_DEV, + /*features*/ 0, + /*sector_count*/ (uint8_t) dxfer_len, + /*lba*/ 0, + /*command*/ ATA_ATA_IDENTIFY, + /*data_ptr*/ (uint8_t *)ptr, + /*dxfer_len*/ dxfer_len, + /*sense_len*/ SSD_FULL_SIZE, + /*timeout*/ timeout ? timeout : 30 * 1000, + /*is48bit*/ 0, + /*devtype*/ devtype); + break; + } + default: + warnx("Unknown disk type %d", devtype); + retval = -1; + goto bailout; + break; /*NOTREACHED*/ + } + + ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; + + retval = cam_send_ccb(dev, ccb); + if (retval != 0) { + warn("error sending %s CCB", (devtype == CC_DT_SCSI) ? + "Test Unit Ready" : "Identify"); + retval = -1; + goto bailout; + } + + if (((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) + && (vp->tur_status == FW_TUR_READY)) { + warnx("Device is not ready"); + if (printerrors) + cam_error_print(dev, ccb, CAM_ESF_ALL, + CAM_EPF_ALL, stderr); + retval = 1; + goto bailout; + } else if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) + && (vp->tur_status == FW_TUR_NOT_READY)) { + warnx("Device cannot have media loaded when firmware is " + "downloaded"); + retval = 1; + goto bailout; + } + bailout: + if (ccb != NULL) + cam_freeccb(ccb); + + return (retval); + } + /* * Download firmware stored in buf to cam_dev. If simulation mode * is enabled, only show what packet sizes would be sent to the * device but do not sent any actual packets */ static int ! fw_download_img(struct cam_device *cam_dev, struct fw_vendor *vp, ! char *buf, int img_size, int sim_mode, int printerrors, int quiet, ! int retry_count, int timeout, const char *imgname, ! camcontrol_devtype devtype) { struct scsi_write_buffer cdb; progress_t progress; ! int size = 0; ! union ccb *ccb = NULL; int pkt_count = 0; int max_pkt_size; u_int32_t pkt_size = 0; char *pkt_ptr = buf; u_int32_t offset; int last_pkt = 0; ! int retval = 0; ! ! /* ! * Check to see whether the device is ready to accept a firmware ! * download. ! */ ! retval = fw_check_device_ready(cam_dev, devtype, vp, printerrors, ! timeout); ! if (retval != 0) ! goto bailout; if ((ccb = cam_getccb(cam_dev)) == NULL) { warnx("Could not allocate CCB"); ! retval = 1; ! goto bailout; } ! bzero(&(&ccb->ccb_h)[1], ! sizeof(union ccb) - sizeof(struct ccb_hdr)); max_pkt_size = vp->max_pkt_size; ! if (max_pkt_size == 0) max_pkt_size = UNKNOWN_MAX_PKT_SIZE; ! ! pkt_size = max_pkt_size; progress_init(&progress, imgname, size = img_size); /* Download single fw packets. */ do { *************** *** 321,376 **** pkt_size = img_size; } progress_update(&progress, size - img_size); ! progress_draw(&progress); bzero(&cdb, sizeof(cdb)); ! if (strcmp(type, "scsi") == 0) { cdb.opcode = WRITE_BUFFER; cdb.control = 0; /* Parameter list length. */ scsi_ulto3b(pkt_size, &cdb.length[0]); offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0; scsi_ulto3b(offset, &cdb.offset[0]); ! cdb.byte2 = last_pkt ? vp->cdb_byte2_last : vp->cdb_byte2; cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0; /* Zero out payload of ccb union after ccb header. */ ! bzero((u_char *)ccb + sizeof(struct ccb_hdr), sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); ! /* Copy previously constructed cdb into ccb_scsiio struct. */ bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0], sizeof(struct scsi_write_buffer)); /* Fill rest of ccb_scsiio struct. */ ! if (!sim_mode) { ! cam_fill_csio(&ccb->csio, /* ccb_scsiio */ ! retry_count, /* retries */ ! NULL, /* cbfcnp */ ! CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags */ ! CAM_TAG_ACTION_NONE, /* tag_action */ ! (u_char *)pkt_ptr, /* data_ptr */ ! pkt_size, /* dxfer_len */ ! SSD_FULL_SIZE, /* sense_len */ ! sizeof(struct scsi_write_buffer), /* cdb_len */ ! timeout ? timeout : CMD_TIMEOUT); /* timeout */ ! } ! } else if (strcmp(type, "ata") == 0) { ! bzero(&(&ccb->ccb_h)[1], ! sizeof(struct ccb_ataio) - sizeof(struct ccb_hdr)); ! if (!sim_mode) { ! uint32_t off; ! cam_fill_ataio(&ccb->ataio, ! (last_pkt) ? 256 : retry_count, ! NULL, ! /*flags*/CAM_DIR_OUT | CAM_DEV_QFRZDIS, ! CAM_TAG_ACTION_NONE, ! /*data_ptr*/(uint8_t *)pkt_ptr, ! /*dxfer_len*/pkt_size, ! timeout ? timeout : 30 * 1000); ! off = (uint32_t)(pkt_ptr - buf); ! ata_28bit_cmd(&ccb->ataio, ATA_DOWNLOAD_MICROCODE, ! USE_OFFSETS_FEATURE, ! ATA_MAKE_LBA(off, pkt_size), ! ATA_MAKE_SECTORS(pkt_size)); ! } } if (!sim_mode) { /* Execute the command. */ --- 803,875 ---- pkt_size = img_size; } progress_update(&progress, size - img_size); ! if (((sim_mode == 0) && (quiet == 0)) ! || ((sim_mode != 0) && (printerrors == 0))) ! progress_draw(&progress); bzero(&cdb, sizeof(cdb)); ! switch (devtype) { ! case CC_DT_SCSI: cdb.opcode = WRITE_BUFFER; cdb.control = 0; /* Parameter list length. */ scsi_ulto3b(pkt_size, &cdb.length[0]); offset = vp->inc_cdb_offset ? (pkt_ptr - buf) : 0; scsi_ulto3b(offset, &cdb.offset[0]); ! cdb.byte2 = last_pkt ? vp->cdb_byte2_last : ! vp->cdb_byte2; cdb.buffer_id = vp->inc_cdb_buffer_id ? pkt_count : 0; /* Zero out payload of ccb union after ccb header. */ ! bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); ! /* ! * Copy previously constructed cdb into ccb_scsiio ! * struct. ! */ bcopy(&cdb, &ccb->csio.cdb_io.cdb_bytes[0], sizeof(struct scsi_write_buffer)); /* Fill rest of ccb_scsiio struct. */ ! cam_fill_csio(&ccb->csio, /* ccb_scsiio*/ ! retry_count, /* retries*/ ! NULL, /* cbfcnp*/ ! CAM_DIR_OUT | CAM_DEV_QFRZDIS, /* flags*/ ! CAM_TAG_ACTION_NONE, /* tag_action*/ ! (u_char *)pkt_ptr, /* data_ptr*/ ! pkt_size, /* dxfer_len*/ ! SSD_FULL_SIZE, /* sense_len*/ ! sizeof(struct scsi_write_buffer), /* cdb_len*/ ! timeout ? timeout : WB_TIMEOUT); /* timeout*/ ! break; ! case CC_DT_ATA: ! case CC_DT_ATA_BEHIND_SCSI: { ! uint32_t off; ! ! off = (uint32_t)(pkt_ptr - buf); ! build_ata_cmd(ccb, ! /*retry_count*/ retry_count, ! /*flags*/ CAM_DIR_OUT | CAM_DEV_QFRZDIS, ! /*tag_action*/ CAM_TAG_ACTION_NONE, ! /*protocol*/ AP_PROTO_PIO_OUT, ! /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES | ! AP_FLAG_TLEN_SECT_CNT | ! AP_FLAG_TDIR_TO_DEV, ! /*features*/ USE_OFFSETS_FEATURE, ! /*sector_count*/ ATA_MAKE_SECTORS(pkt_size), ! /*lba*/ ATA_MAKE_LBA(off, pkt_size), ! /*command*/ ATA_DOWNLOAD_MICROCODE, ! /*data_ptr*/ (uint8_t *)pkt_ptr, ! /*dxfer_len*/ pkt_size, ! /*sense_len*/ SSD_FULL_SIZE, ! /*timeout*/ timeout ? timeout : WB_TIMEOUT, ! /*is48bit*/ 0, ! /*devtype*/ devtype); ! break; ! } ! default: ! warnx("Unknown device type %d", devtype); ! retval = 1; ! goto bailout; ! break; /*NOTREACHED*/ } if (!sim_mode) { /* Execute the command. */ *************** *** 379,425 **** CAM_REQ_CMP) { warnx("Error writing image to device"); if (printerrors) ! cam_error_print(cam_dev, ccb, CAM_ESF_ALL, ! CAM_EPF_ALL, stderr); goto bailout; } } /* Prepare next round. */ pkt_count++; pkt_ptr += pkt_size; img_size -= pkt_size; } while(!last_pkt); - progress_complete(&progress, size - img_size); - cam_freeccb(ccb); - return (0); bailout: ! progress_complete(&progress, size - img_size); ! cam_freeccb(ccb); ! return (1); } int fwdownload(struct cam_device *device, int argc, char **argv, ! char *combinedopt, int printerrors, int retry_count, int timeout, ! const char *type) { ! const struct fw_vendor *vp; char *fw_img_path = NULL; ! char *buf; int img_size; int c; int sim_mode = 0; int confirmed = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 's': sim_mode = 1; - confirmed = 1; break; - case 'f': - fw_img_path = optarg; - break; case 'y': confirmed = 1; break; --- 878,933 ---- CAM_REQ_CMP) { warnx("Error writing image to device"); if (printerrors) ! cam_error_print(cam_dev, ccb, ! CAM_ESF_ALL, CAM_EPF_ALL, stderr); ! retval = 1; goto bailout; } + } else if (printerrors) { + cam_error_print(cam_dev, ccb, CAM_ESF_COMMAND, 0, + stdout); } + /* Prepare next round. */ pkt_count++; pkt_ptr += pkt_size; img_size -= pkt_size; } while(!last_pkt); bailout: ! if (quiet == 0) ! progress_complete(&progress, size - img_size); ! if (ccb != NULL) ! cam_freeccb(ccb); ! return (retval); } int fwdownload(struct cam_device *device, int argc, char **argv, ! char *combinedopt, int printerrors, int retry_count, int timeout) { ! struct fw_vendor *vp; char *fw_img_path = NULL; ! struct ata_params *ident_buf = NULL; ! camcontrol_devtype devtype; ! char *buf = NULL; int img_size; int c; int sim_mode = 0; int confirmed = 0; + int quiet = 0; + int retval = 0; while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { + case 'f': + fw_img_path = optarg; + break; + case 'q': + quiet = 1; + break; case 's': sim_mode = 1; break; case 'y': confirmed = 1; break; *************** *** 429,470 **** } if (fw_img_path == NULL) ! errx(1, "you must specify a firmware image file using -f option"); ! vp = fw_get_vendor(device); ! if (vp == NULL) ! errx(1, "NULL vendor"); ! if (vp->type == VENDOR_UNKNOWN) ! warnx("Unsupported device - flashing through an HBA?"); ! buf = fw_read_img(fw_img_path, vp, &img_size); ! if (buf == NULL) ! goto fail; if (!confirmed) { fprintf(stdout, "You are about to download firmware image (%s)" " into the following device:\n", fw_img_path); fprintf(stdout, "\nIt may damage your drive. "); ! if (!get_confirmation()) ! goto fail; } ! if (sim_mode) fprintf(stdout, "Running in simulation mode\n"); if (fw_download_img(device, vp, buf, img_size, sim_mode, printerrors, ! retry_count, timeout, fw_img_path, type) != 0) { fprintf(stderr, "Firmware download failed\n"); ! goto fail; ! } ! else fprintf(stdout, "Firmware download successful\n"); free(buf); ! return (0); ! fail: ! if (buf != NULL) ! free(buf); ! return (1); } --- 937,1036 ---- } if (fw_img_path == NULL) ! errx(1, "you must specify a firmware image file using -f " ! "option"); ! ! retval = get_device_type(device, retry_count, timeout, printerrors, ! &devtype); ! if (retval != 0) ! errx(1, "Unable to determine device type"); ! ! if ((devtype == CC_DT_ATA) ! || (devtype == CC_DT_ATA_BEHIND_SCSI)) { ! union ccb *ccb; ! ! ccb = cam_getccb(device); ! if (ccb == NULL) { ! warnx("couldn't allocate CCB"); ! retval = 1; ! goto bailout; ! } ! ! if (ata_do_identify(device, retry_count, timeout, ccb, ! &ident_buf) != 0) { ! cam_freeccb(ccb); ! retval = 1; ! goto bailout; ! } ! } else if (devtype != CC_DT_SCSI) ! errx(1, "Unsupported device type %d", devtype); ! ! vp = fw_get_vendor(device, ident_buf); ! /* ! * Bail out if we have an unknown vendor and this isn't an ATA ! * disk. For a SCSI disk, we have no chance of working properly ! * with the default values in the VENDOR_UNKNOWN case. For an ATA ! * disk connected via an ATA transport, we may work for drives that ! * support the ATA_DOWNLOAD_MICROCODE command. ! */ ! if (((vp == NULL) ! || (vp->type == VENDOR_UNKNOWN)) ! && (devtype == CC_DT_SCSI)) ! errx(1, "Unsupported device"); ! retval = fw_get_timeout(device, vp, retry_count, timeout); ! if (retval != 0) { ! warnx("Unable to get a firmware download timeout value"); ! goto bailout; ! } ! buf = fw_read_img(device, retry_count, timeout, quiet, fw_img_path, ! vp, &img_size); ! if (buf == NULL) { ! retval = 1; ! goto bailout; ! } if (!confirmed) { fprintf(stdout, "You are about to download firmware image (%s)" " into the following device:\n", fw_img_path); + if (devtype == CC_DT_SCSI) { + if (scsidoinquiry(device, argc, argv, combinedopt, 0, + 5000) != 0) { + warnx("Error sending inquiry"); + retval = 1; + goto bailout; + } + } else { + printf("%s%d: ", device->device_name, + device->dev_unit_num); + ata_print_ident(ident_buf); + camxferrate(device); + free(ident_buf); + } + fprintf(stdout, "Using a timeout of %u ms, which is %s.\n", + vp->timeout_ms, + fw_timeout_desc_table[vp->timeout_type].timeout_desc); fprintf(stdout, "\nIt may damage your drive. "); ! if (!get_confirmation()) { ! retval = 1; ! goto bailout; ! } } ! if ((sim_mode != 0) && (quiet == 0)) fprintf(stdout, "Running in simulation mode\n"); if (fw_download_img(device, vp, buf, img_size, sim_mode, printerrors, ! quiet, retry_count, vp->timeout_ms, fw_img_path, devtype) != 0) { fprintf(stderr, "Firmware download failed\n"); ! retval = 1; ! goto bailout; ! } else if (quiet == 0) fprintf(stdout, "Firmware download successful\n"); + bailout: free(buf); ! return (retval); } *** src/sbin/camcontrol/progress.c.orig --- src/sbin/camcontrol/progress.c *************** *** 124,129 **** --- 124,130 ---- * stars should contain at least * sizeof(buf) - BAROVERHEAD entries */ + #define MIN_BAR_LEN 10 static const char stars[] = "*****************************************************************************" "*****************************************************************************" *************** *** 134,146 **** uint64_t bytespersec; uint64_t abbrevsize; int64_t secsleft; ! size_t barlength; size_t starc; char hours[12]; char buf[256]; int len; ! barlength = MIN(sizeof(buf) - 1, (unsigned)prog->ttywidth) - BAROVERHEAD - strlen(prog->prefix); starc = (barlength * prog->percent) / 100; abbrevsize = prog->done; for (bytesabbrev = 0; abbrevsize >= 100000 && bytesabbrev < NSUFFIXES; bytesabbrev++) { --- 135,165 ---- uint64_t bytespersec; uint64_t abbrevsize; int64_t secsleft; ! ssize_t barlength; size_t starc; char hours[12]; char buf[256]; int len; + int prefix_len; ! prefix_len = strlen(prog->prefix); ! barlength = MIN(sizeof(buf) - 1, (unsigned)prog->ttywidth) - ! BAROVERHEAD - prefix_len; ! if (barlength < MIN_BAR_LEN) { ! int tmp_prefix_len; ! /* ! * In this case the TTY width is too small or the prefix is ! * too large for a progress bar. We'll try decreasing the ! * prefix length. ! */ ! barlength = MIN_BAR_LEN; ! tmp_prefix_len = MIN(sizeof(buf) - 1,(unsigned)prog->ttywidth) - ! BAROVERHEAD - MIN_BAR_LEN; ! if (tmp_prefix_len > 0) ! prefix_len = tmp_prefix_len; ! else ! prefix_len = 0; ! } starc = (barlength * prog->percent) / 100; abbrevsize = prog->done; for (bytesabbrev = 0; abbrevsize >= 100000 && bytesabbrev < NSUFFIXES; bytesabbrev++) { *************** *** 171,178 **** } secs = secsleft % SECSPERHOUR; len = snprintf(buf, sizeof(buf), ! "\r%s %3lld%% |%.*s%*s| %5lld %-3s %3lld.%02d %.2sB/s %s%02d:%02d ETA", ! (prog->prefix) ? prog->prefix : "", (long long)prog->percent, (int)starc, stars, (int)(barlength - starc), "", (long long)abbrevsize, --- 190,197 ---- } secs = secsleft % SECSPERHOUR; len = snprintf(buf, sizeof(buf), ! "\r%.*s %3lld%% |%.*s%*s| %5lld %-3s %3lld.%02d %.2sB/s %s%02d:%02d ETA", ! prefix_len, (prog->prefix) ? prog->prefix : "", (long long)prog->percent, (int)starc, stars, (int)(barlength - starc), "", (long long)abbrevsize, *** src/sys/cam/scsi/scsi_all.c.orig --- src/sys/cam/scsi/scsi_all.c *************** *** 8418,8423 **** --- 8418,8455 ---- timeout); } + void + scsi_report_supported_opcodes(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, union ccb *), + uint8_t tag_action, int options, int req_opcode, + int req_service_action, uint8_t *data_ptr, + uint32_t dxfer_len, int sense_len, int timeout) + { + struct scsi_report_supported_opcodes *scsi_cmd; + + scsi_cmd = (struct scsi_report_supported_opcodes *) + &csio->cdb_io.cdb_bytes; + bzero(scsi_cmd, sizeof(*scsi_cmd)); + + scsi_cmd->opcode = MAINTENANCE_IN; + scsi_cmd->service_action = REPORT_SUPPORTED_OPERATION_CODES; + scsi_cmd->options = options; + scsi_cmd->requested_opcode = req_opcode; + scsi_ulto2b(req_service_action, scsi_cmd->requested_service_action); + scsi_ulto4b(dxfer_len, scsi_cmd->length); + + cam_fill_csio(csio, + retries, + cbfcnp, + /*flags*/CAM_DIR_IN, + tag_action, + data_ptr, + dxfer_len, + sense_len, + sizeof(*scsi_cmd), + timeout); + } + /* * Try make as good a match as possible with * available sub drivers *** src/sys/cam/scsi/scsi_all.h.orig --- src/sys/cam/scsi/scsi_all.h *************** *** 1457,1462 **** --- 1457,1467 ---- uint8_t reserved; uint8_t support; #define RSO_ONE_CTDP 0x80 + #define RSO_ONE_SUP_MASK 0x07 + #define RSO_ONE_SUP_UNAVAIL 0x00 + #define RSO_ONE_SUP_NOT_SUP 0x01 + #define RSO_ONE_SUP_AVAIL 0x03 + #define RSO_ONE_SUP_VENDOR 0x05 uint8_t cdb_length[2]; uint8_t cdb_usage[]; }; *************** *** 3967,3972 **** --- 3972,3985 ---- uint32_t dxfer_len, int sense_len, int timeout); + void scsi_report_supported_opcodes(struct ccb_scsiio *csio, uint32_t retries, + void (*cbfcnp)(struct cam_periph *, + union ccb *), + uint8_t tag_action, int options, + int req_opcode, int req_service_action, + uint8_t *data_ptr, uint32_t dxfer_len, + int sense_len, int timeout); + int scsi_inquiry_match(caddr_t inqbuffer, caddr_t table_entry); int scsi_static_inquiry_match(caddr_t inqbuffer, caddr_t table_entry);