/* * 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 verbose, int retry_count, int timeout) { struct scsi_write_buffer cdb; union ccb *ccb; int pkt_count = 0; u_int32_t pkt_size = 0; char *pkt_ptr = buf; u_int32_t offset; int last_pkt = 0; if ((ccb = cam_getccb(cam_dev)) == NULL) { warnx("Could not allocate CCB"); return (1); } scsi_test_unit_ready(&ccb->csio, 0, NULL, MSG_SIMPLE_Q_TAG, SSD_FULL_SIZE, 5000); /* Disable freezing the device queue. */ ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error sending test unit ready"); if (verbose) 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 (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return (1); } pkt_size = vp->max_pkt_size; if (verbose || sim_mode) { fprintf(stdout, "--------------------------------------------------\n"); fprintf(stdout, "PktNo. PktSize BytesRemaining LastPkt\n"); fprintf(stdout, "--------------------------------------------------\n"); } /* Download single fw packets. */ do { if (img_size <= vp->max_pkt_size) { last_pkt = 1; pkt_size = img_size; } if (verbose || sim_mode) fprintf(stdout, "%3u %5u (0x%05X) %7u (0x%06X) " "%d\n", pkt_count, pkt_size, pkt_size, img_size - pkt_size, img_size - pkt_size, last_pkt); bzero(&cdb, sizeof(cdb)); 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 */ /* Execute the command. */ if (cam_send_ccb(cam_dev, ccb) < 0) { warnx("Error writing image to device"); if (verbose) 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); if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { if (verbose) cam_error_print(cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); goto bailout; } cam_freeccb(ccb); return (0); bailout: cam_freeccb(ccb); return (1); }
/* * 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); 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 { if (img_size <= max_pkt_size) { last_pkt = 1; 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. */ if (cam_send_ccb(cam_dev, ccb) < 0 || (ccb->ccb_h.status & CAM_STATUS_MASK) != 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); }
/* Executes SCSI command (or at least forwards it to lower layers). * Clears os_err field prior to active call (whose result may set it * again). */ int do_scsi_pt(struct sg_pt_base * vp, int device_fd, int time_secs, int verbose) { int fd = device_fd - FREEBSD_FDOFFSET; struct sg_pt_freebsd_scsi * ptp = &vp->impl; struct freebsd_dev_channel *fdchan; union ccb *ccb; int len, timout_ms; if (NULL == sg_warnings_strm) sg_warnings_strm = stderr; ptp->os_err = 0; if (ptp->in_err) { if (verbose) fprintf(sg_warnings_strm, "Replicated or unused set_scsi_pt...\n"); return SCSI_PT_DO_BAD_PARAMS; } if (NULL == ptp->cdb) { if (verbose) fprintf(sg_warnings_strm, "No command (cdb) given\n"); return SCSI_PT_DO_BAD_PARAMS; } if ((fd < 0) || (fd >= FREEBSD_MAXDEV)) { if (verbose) fprintf(sg_warnings_strm, "Bad file descriptor\n"); ptp->os_err = ENODEV; return -ptp->os_err; } fdchan = devicetable[fd]; if (NULL == fdchan) { if (verbose) fprintf(sg_warnings_strm, "File descriptor closed??\n"); ptp->os_err = ENODEV; return -ptp->os_err; } if (NULL == fdchan->cam_dev) { if (verbose) fprintf(sg_warnings_strm, "No open CAM device\n"); return SCSI_PT_DO_BAD_PARAMS; } if (! (ccb = cam_getccb(fdchan->cam_dev))) { if (verbose) fprintf(sg_warnings_strm, "cam_getccb: failed\n"); ptp->os_err = ENOMEM; return -ptp->os_err; } ptp->ccb = ccb; // clear out structure, except for header that was filled in for us bzero(&(&ccb->ccb_h)[1], sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); timout_ms = (time_secs > 0) ? (time_secs * 1000) : DEF_TIMEOUT; cam_fill_csio(&ccb->csio, /* retries */ 1, /* cbfcnp */ NULL, /* flags */ ptp->dxfer_dir, /* tagaction */ MSG_SIMPLE_Q_TAG, /* dataptr */ ptp->dxferp, /* datalen */ ptp->dxfer_len, /* senselen */ ptp->sense_len, /* cdblen */ ptp->cdb_len, /* timeout (millisecs) */ timout_ms); memcpy(ccb->csio.cdb_io.cdb_bytes, ptp->cdb, ptp->cdb_len); if (cam_send_ccb(fdchan->cam_dev, ccb) < 0) { if (verbose) { warn("error sending SCSI ccb"); #if __FreeBSD_version > 500000 cam_error_print(fdchan->cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); #endif } cam_freeccb(ptp->ccb); ptp->ccb = NULL; ptp->os_err = EIO; return -ptp->os_err; } if (((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_CMP) || ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR)) { ptp->scsi_status = ccb->csio.scsi_status; ptp->resid = ccb->csio.resid; ptp->sense_resid = ccb->csio.sense_resid; if ((SAM_STAT_CHECK_CONDITION == ptp->scsi_status) || (SAM_STAT_COMMAND_TERMINATED == ptp->scsi_status)) { len = ptp->sense_len - ptp->sense_resid; if (len) memcpy(ptp->sense, &(ccb->csio.sense_data), len); } } else ptp->transport_err = 1; ptp->cam_dev = fdchan->cam_dev; // for error processing return 0; }
int smp_send_req(const struct smp_target_obj * tobj, struct smp_req_resp * rresp, int verbose) { union ccb *ccb; struct tobj_cam_t * tcp; int retval, emsk; int flags = 0; if ((NULL == tobj) || (0 == tobj->opened) || (NULL == tobj->vp)) { if (verbose) fprintf(stderr, "smp_send_req: nothing open??\n"); return -1; } if (I_CAM != tobj->interface_selector) { fprintf(stderr, "smp_send_req: unknown transport [%d]\n", tobj->interface_selector); return -1; } tcp = (struct tobj_cam_t *)tobj->vp; if (! (ccb = cam_getccb(tcp->cam_dev))) { fprintf(stderr, "cam_getccb: failed\n"); return -1; } // clear out structure, except for header that was filled in for us bzero(&(&ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); flags |= CAM_DEV_QFRZDIS; /* CAM does not want request_len including CRC */ cam_fill_smpio(&ccb->smpio, /*retries*/ 2, /* guess */ /*cbfcnp*/ NULL, /*flags*/ flags, /*smp_request*/ rresp->request, /*smp_request_len*/ rresp->request_len - 4, /*smp_response*/ rresp->response, /*smp_response_len*/ rresp->max_response_len, /*timeout*/ 5000); /* milliseconds ? */ ccb->smpio.flags = SMP_FLAG_NONE; emsk = 0; if (((retval = cam_send_ccb(tcp->cam_dev, ccb)) < 0) || ((((emsk = (ccb->ccb_h.status & CAM_STATUS_MASK))) != CAM_REQ_CMP) && (emsk != CAM_SMP_STATUS_ERROR))) { cam_error_print(tcp->cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); cam_freeccb(ccb); return -1; } if (((emsk == CAM_REQ_CMP) || (emsk == CAM_SMP_STATUS_ERROR)) && (rresp->max_response_len > 0)) { if ((emsk == CAM_SMP_STATUS_ERROR) && (verbose > 3)) cam_error_print(tcp->cam_dev, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); rresp->act_response_len = -1; cam_freeccb(ccb); return 0; } else { fprintf(stderr, "smp_send_req(cam): not sure how it got here\n"); cam_freeccb(ccb); return emsk ? emsk : -1; } }
int scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt, int retry_count, int timeout, int verbosemode, int err_recover) { union ccb *ccb = NULL; int attr_num = -1; #if 0 int num_attrs = 0; #endif int start_attr = 0; int cached_attr = 0; int read_service_action = -1; int read_attr = 0, write_attr = 0; int element_address = 0; int element_type = ELEMENT_TYPE_ALL; int partition = 0; int logical_volume = 0; char *endptr; uint8_t *data_buf = NULL; uint32_t dxfer_len = UINT16_MAX - 1; uint32_t valid_len; uint32_t output_format; STAILQ_HEAD(, scsi_attr_desc) write_attr_list; int error = 0; int c; ccb = cam_getccb(device); if (ccb == NULL) { warnx("%s: error allocating CCB", __func__); error = 1; goto bailout; } bzero(&(&ccb->ccb_h)[1], sizeof(union ccb) - sizeof(struct ccb_hdr)); STAILQ_INIT(&write_attr_list); /* * By default, when displaying attribute values, we trim out * non-ASCII characters in ASCII fields. We display all fields * (description, attribute number, attribute size, and readonly * status). We default to displaying raw text. * * XXX KDM need to port this to stable/10 and newer FreeBSD * versions that have iconv built in and can convert codesets. */ output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM | SCSI_ATTR_OUTPUT_FIELD_ALL | SCSI_ATTR_OUTPUT_TEXT_RAW; data_buf = malloc(dxfer_len); if (data_buf == NULL) { warn("%s: error allocating %u bytes", __func__, dxfer_len); error = 1; goto bailout; } while ((c = getopt(argc, argv, combinedopt)) != -1) { switch (c) { case 'a': attr_num = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid attribute number %s", __func__, optarg); error = 1; goto bailout; } start_attr = attr_num; break; case 'c': cached_attr = 1; break; case 'e': element_address = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid element address %s", __func__, optarg); error = 1; goto bailout; } break; case 'F': { scsi_nv_status status; scsi_attrib_output_flags new_outflags; int entry_num = 0; char *tmpstr; if (isdigit(optarg[0])) { output_format = strtoul(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid numeric output " "format argument %s", __func__, optarg); error = 1; goto bailout; } break; } new_outflags = SCSI_ATTR_OUTPUT_NONE; while ((tmpstr = strsep(&optarg, ",")) != NULL) { status = scsi_get_nv(output_format_map, sizeof(output_format_map) / sizeof(output_format_map[0]), tmpstr, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) new_outflags |= output_format_map[entry_num].value; else { warnx("%s: %s format option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", tmpstr); error = 1; goto bailout; } } output_format = new_outflags; break; } case 'p': partition = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid partition number %s", __func__, optarg); error = 1; goto bailout; } break; case 'r': { scsi_nv_status status; int entry_num = 0; status = scsi_get_nv(sa_map, sizeof(sa_map) / sizeof(sa_map[0]), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) read_service_action = sa_map[entry_num].value; else { warnx("%s: %s %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "service action", optarg); error = 1; goto bailout; } read_attr = 1; break; } case 's': start_attr = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid starting attr argument %s", __func__, optarg); error = 1; goto bailout; } break; case 'T': { scsi_nv_status status; int entry_num = 0; status = scsi_get_nv(elem_type_map, sizeof(elem_type_map) / sizeof(elem_type_map[0]), optarg, &entry_num, SCSI_NV_FLAG_IG_CASE); if (status == SCSI_NV_FOUND) element_type = elem_type_map[entry_num].value; else { warnx("%s: %s %s option %s", __func__, (status == SCSI_NV_AMBIGUOUS) ? "ambiguous" : "invalid", "element type", optarg); error = 1; goto bailout; } break; } case 'w': warnx("%s: writing attributes is not implemented yet", __func__); error = 1; goto bailout; break; case 'V': logical_volume = strtol(optarg, &endptr, 0); if (*endptr != '\0') { warnx("%s: invalid logical volume argument %s", __func__, optarg); error = 1; goto bailout; } break; default: break; } } /* * Default to reading attributes */ if (((read_attr == 0) && (write_attr == 0)) || ((read_attr != 0) && (write_attr != 0))) { warnx("%s: Must specify either -r or -w", __func__); error = 1; goto bailout; } if (read_attr != 0) { scsi_read_attribute(&ccb->csio, /*retries*/ retry_count, /*cbfcnp*/ NULL, /*tag_action*/ MSG_SIMPLE_Q_TAG, /*service_action*/ read_service_action, /*element*/ element_address, /*elem_type*/ element_type, /*logical_volume*/ logical_volume, /*partition*/ partition, /*first_attribute*/ start_attr, /*cache*/ cached_attr, /*data_ptr*/ data_buf, /*length*/ dxfer_len, /*sense_len*/ SSD_FULL_SIZE, /*timeout*/ timeout ? timeout : 60000); #if 0 } else { #endif } ccb->ccb_h.flags |= CAM_DEV_QFRZDIS; if (err_recover != 0) ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER; if (cam_send_ccb(device, ccb) < 0) { warn("error sending %s ATTRIBUTE", (read_attr != 0) ? "READ" : "WRITE"); if (verbosemode != 0) { cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL, stderr); } error = 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); } error = 1; goto bailout; } if (read_attr == 0) goto bailout; valid_len = dxfer_len - ccb->csio.resid; switch (read_service_action) { case SRA_SA_ATTR_VALUES: { uint32_t len_left, hdr_len, cur_len; struct scsi_read_attribute_values *hdr; struct scsi_mam_attribute_header *cur_id; char error_str[512]; uint8_t *cur_pos; struct sbuf *sb; hdr = (struct scsi_read_attribute_values *)data_buf; if (valid_len < sizeof(*hdr)) { fprintf(stdout, "No attributes returned.\n"); error = 0; goto bailout; } sb = sbuf_new_auto(); if (sb == NULL) { warn("%s: Unable to allocate sbuf", __func__); error = 1; goto bailout; } /* * XXX KDM grab more data if it is available. */ hdr_len = scsi_4btoul(hdr->length); for (len_left = MIN(valid_len, hdr_len), cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id); len_left -= cur_len, cur_pos += cur_len) { int cur_attr_num; cur_id = (struct scsi_mam_attribute_header *)cur_pos; cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id); cur_attr_num = scsi_2btoul(cur_id->id); if ((attr_num != -1) && (cur_attr_num != attr_num)) continue; error = scsi_attrib_sbuf(sb, cur_id, len_left, /*user_table*/ NULL, /*num_user_entries*/ 0, /*prefer_user_table*/ 0, output_format, error_str, sizeof(error_str)); if (error != 0) { warnx("%s: %s", __func__, error_str); sbuf_delete(sb); error = 1; goto bailout; } if (attr_num != -1) break; } sbuf_finish(sb); fprintf(stdout, "%s", sbuf_data(sb)); sbuf_delete(sb); break; } case SRA_SA_SUPPORTED_ATTRS: case SRA_SA_ATTR_LIST: { uint32_t len_left, hdr_len; struct scsi_attrib_list_header *hdr; struct scsi_attrib_table_entry *entry = NULL; const char *sa_name = "Supported Attributes"; const char *at_name = "Available Attributes"; int attr_id; uint8_t *cur_id; hdr = (struct scsi_attrib_list_header *)data_buf; if (valid_len < sizeof(*hdr)) { fprintf(stdout, "No %s\n", (read_service_action == SRA_SA_SUPPORTED_ATTRS)? sa_name : at_name); error = 0; goto bailout; } fprintf(stdout, "%s:\n", (read_service_action == SRA_SA_SUPPORTED_ATTRS) ? sa_name : at_name); hdr_len = scsi_4btoul(hdr->length); for (len_left = MIN(valid_len, hdr_len), cur_id = &hdr->first_attr_0[0]; len_left > 1; len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) { attr_id = scsi_2btoul(cur_id); if ((attr_num != -1) && (attr_id != attr_num)) continue; entry = scsi_get_attrib_entry(attr_id); fprintf(stdout, "0x%.4x", attr_id); if (entry == NULL) fprintf(stdout, "\n"); else fprintf(stdout, ": %s\n", entry->desc); if (attr_num != -1) break; } break; } case SRA_SA_PART_LIST: case SRA_SA_LOG_VOL_LIST: { struct scsi_attrib_lv_list *lv_list; const char *partition_name = "Partition"; const char *lv_name = "Logical Volume"; if (valid_len < sizeof(*lv_list)) { fprintf(stdout, "No %s list returned\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name); error = 0; goto bailout; } lv_list = (struct scsi_attrib_lv_list *)data_buf; fprintf(stdout, "First %s: %d\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name, lv_list->first_lv_number); fprintf(stdout, "Number of %ss: %d\n", (read_service_action == SRA_SA_PART_LIST) ? partition_name : lv_name, lv_list->num_logical_volumes); break; } default: break; } bailout: if (ccb != NULL) cam_freeccb(ccb); free(data_buf); return (error); }