15672fac9SKenneth D. Merry /*-
25672fac9SKenneth D. Merry * Copyright (c) 2014 Spectra Logic Corporation
35672fac9SKenneth D. Merry * All rights reserved.
45672fac9SKenneth D. Merry *
55672fac9SKenneth D. Merry * Redistribution and use in source and binary forms, with or without
65672fac9SKenneth D. Merry * modification, are permitted provided that the following conditions
75672fac9SKenneth D. Merry * are met:
85672fac9SKenneth D. Merry * 1. Redistributions of source code must retain the above copyright
95672fac9SKenneth D. Merry * notice, this list of conditions, and the following disclaimer,
105672fac9SKenneth D. Merry * without modification.
115672fac9SKenneth D. Merry * 2. Redistributions in binary form must reproduce at minimum a disclaimer
125672fac9SKenneth D. Merry * substantially similar to the "NO WARRANTY" disclaimer below
135672fac9SKenneth D. Merry * ("Disclaimer") and any redistribution must be conditioned upon
145672fac9SKenneth D. Merry * including a substantially similar Disclaimer requirement for further
155672fac9SKenneth D. Merry * binary redistribution.
165672fac9SKenneth D. Merry *
175672fac9SKenneth D. Merry * NO WARRANTY
185672fac9SKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
195672fac9SKenneth D. Merry * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
205672fac9SKenneth D. Merry * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
215672fac9SKenneth D. Merry * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
225672fac9SKenneth D. Merry * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
235672fac9SKenneth D. Merry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
245672fac9SKenneth D. Merry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
255672fac9SKenneth D. Merry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
265672fac9SKenneth D. Merry * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
275672fac9SKenneth D. Merry * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
285672fac9SKenneth D. Merry * POSSIBILITY OF SUCH DAMAGES.
295672fac9SKenneth D. Merry *
305672fac9SKenneth D. Merry * Authors: Ken Merry (Spectra Logic Corporation)
315672fac9SKenneth D. Merry */
325672fac9SKenneth D. Merry /*
335672fac9SKenneth D. Merry * SCSI Read and Write Attribute support for camcontrol(8).
345672fac9SKenneth D. Merry */
355672fac9SKenneth D. Merry
365672fac9SKenneth D. Merry #include <sys/cdefs.h>
375672fac9SKenneth D. Merry #include <sys/ioctl.h>
385672fac9SKenneth D. Merry #include <sys/stdint.h>
39*7028e630SElyes Haouas #include <sys/param.h>
405672fac9SKenneth D. Merry #include <sys/endian.h>
415672fac9SKenneth D. Merry #include <sys/sbuf.h>
425672fac9SKenneth D. Merry #include <sys/queue.h>
435672fac9SKenneth D. Merry #include <sys/chio.h>
445672fac9SKenneth D. Merry
455672fac9SKenneth D. Merry #include <stdio.h>
465672fac9SKenneth D. Merry #include <stdlib.h>
475672fac9SKenneth D. Merry #include <inttypes.h>
485672fac9SKenneth D. Merry #include <unistd.h>
495672fac9SKenneth D. Merry #include <string.h>
505672fac9SKenneth D. Merry #include <strings.h>
515672fac9SKenneth D. Merry #include <fcntl.h>
525672fac9SKenneth D. Merry #include <ctype.h>
535672fac9SKenneth D. Merry #include <limits.h>
545672fac9SKenneth D. Merry #include <err.h>
555672fac9SKenneth D. Merry #include <locale.h>
565672fac9SKenneth D. Merry
575672fac9SKenneth D. Merry #include <cam/cam.h>
585672fac9SKenneth D. Merry #include <cam/cam_debug.h>
595672fac9SKenneth D. Merry #include <cam/cam_ccb.h>
605672fac9SKenneth D. Merry #include <cam/scsi/scsi_all.h>
615672fac9SKenneth D. Merry #include <cam/scsi/scsi_pass.h>
625672fac9SKenneth D. Merry #include <cam/scsi/scsi_ch.h>
635672fac9SKenneth D. Merry #include <cam/scsi/scsi_message.h>
645672fac9SKenneth D. Merry #include <camlib.h>
655672fac9SKenneth D. Merry #include "camcontrol.h"
665672fac9SKenneth D. Merry
675672fac9SKenneth D. Merry #if 0
685672fac9SKenneth D. Merry struct scsi_attr_desc {
695672fac9SKenneth D. Merry int attr_id;
705672fac9SKenneth D. Merry
715672fac9SKenneth D. Merry STAILQ_ENTRY(scsi_attr_desc) links;
725672fac9SKenneth D. Merry };
735672fac9SKenneth D. Merry #endif
745672fac9SKenneth D. Merry
755672fac9SKenneth D. Merry static struct scsi_nv elem_type_map[] = {
765672fac9SKenneth D. Merry { "all", ELEMENT_TYPE_ALL },
775672fac9SKenneth D. Merry { "picker", ELEMENT_TYPE_MT },
785672fac9SKenneth D. Merry { "slot", ELEMENT_TYPE_ST },
795672fac9SKenneth D. Merry { "portal", ELEMENT_TYPE_IE },
805672fac9SKenneth D. Merry { "drive", ELEMENT_TYPE_DT },
815672fac9SKenneth D. Merry };
825672fac9SKenneth D. Merry
835672fac9SKenneth D. Merry static struct scsi_nv sa_map[] = {
845672fac9SKenneth D. Merry { "attr_values", SRA_SA_ATTR_VALUES },
855672fac9SKenneth D. Merry { "attr_list", SRA_SA_ATTR_LIST },
865672fac9SKenneth D. Merry { "lv_list", SRA_SA_LOG_VOL_LIST },
875672fac9SKenneth D. Merry { "part_list", SRA_SA_PART_LIST },
885672fac9SKenneth D. Merry { "supp_attr", SRA_SA_SUPPORTED_ATTRS }
895672fac9SKenneth D. Merry };
905672fac9SKenneth D. Merry
915672fac9SKenneth D. Merry static struct scsi_nv output_format_map[] = {
925672fac9SKenneth D. Merry { "text_esc", SCSI_ATTR_OUTPUT_TEXT_ESC },
935672fac9SKenneth D. Merry { "text_raw", SCSI_ATTR_OUTPUT_TEXT_RAW },
945672fac9SKenneth D. Merry { "nonascii_esc", SCSI_ATTR_OUTPUT_NONASCII_ESC },
955672fac9SKenneth D. Merry { "nonascii_trim", SCSI_ATTR_OUTPUT_NONASCII_TRIM },
965672fac9SKenneth D. Merry { "nonascii_raw", SCSI_ATTR_OUTPUT_NONASCII_RAW },
975672fac9SKenneth D. Merry { "field_all", SCSI_ATTR_OUTPUT_FIELD_ALL },
985672fac9SKenneth D. Merry { "field_none", SCSI_ATTR_OUTPUT_FIELD_NONE },
995672fac9SKenneth D. Merry { "field_desc", SCSI_ATTR_OUTPUT_FIELD_DESC },
1005672fac9SKenneth D. Merry { "field_num", SCSI_ATTR_OUTPUT_FIELD_NUM },
1015672fac9SKenneth D. Merry { "field_size", SCSI_ATTR_OUTPUT_FIELD_SIZE },
1025672fac9SKenneth D. Merry { "field_rw", SCSI_ATTR_OUTPUT_FIELD_RW },
1035672fac9SKenneth D. Merry };
1045672fac9SKenneth D. Merry
1055672fac9SKenneth D. Merry int
scsiattrib(struct cam_device * device,int argc,char ** argv,char * combinedopt,int task_attr,int retry_count,int timeout,int verbosemode,int err_recover)1065672fac9SKenneth D. Merry scsiattrib(struct cam_device *device, int argc, char **argv, char *combinedopt,
107492a2ef5SKenneth D. Merry int task_attr, int retry_count, int timeout, int verbosemode,
108492a2ef5SKenneth D. Merry int err_recover)
1095672fac9SKenneth D. Merry {
1105672fac9SKenneth D. Merry union ccb *ccb = NULL;
1115672fac9SKenneth D. Merry int attr_num = -1;
1125672fac9SKenneth D. Merry #if 0
1135672fac9SKenneth D. Merry int num_attrs = 0;
1145672fac9SKenneth D. Merry #endif
1155672fac9SKenneth D. Merry int start_attr = 0;
1165672fac9SKenneth D. Merry int cached_attr = 0;
1175672fac9SKenneth D. Merry int read_service_action = -1;
1185672fac9SKenneth D. Merry int read_attr = 0, write_attr = 0;
1195672fac9SKenneth D. Merry int element_address = 0;
1205672fac9SKenneth D. Merry int element_type = ELEMENT_TYPE_ALL;
1215672fac9SKenneth D. Merry int partition = 0;
1225672fac9SKenneth D. Merry int logical_volume = 0;
1235672fac9SKenneth D. Merry char *endptr;
1245672fac9SKenneth D. Merry uint8_t *data_buf = NULL;
1255672fac9SKenneth D. Merry uint32_t dxfer_len = UINT16_MAX - 1;
1265672fac9SKenneth D. Merry uint32_t valid_len;
1275672fac9SKenneth D. Merry uint32_t output_format;
1285672fac9SKenneth D. Merry STAILQ_HEAD(, scsi_attr_desc) write_attr_list;
1295672fac9SKenneth D. Merry int error = 0;
1305672fac9SKenneth D. Merry int c;
1315672fac9SKenneth D. Merry
1325672fac9SKenneth D. Merry ccb = cam_getccb(device);
1335672fac9SKenneth D. Merry if (ccb == NULL) {
1345672fac9SKenneth D. Merry warnx("%s: error allocating CCB", __func__);
1355672fac9SKenneth D. Merry error = 1;
1365672fac9SKenneth D. Merry goto bailout;
1375672fac9SKenneth D. Merry }
1385672fac9SKenneth D. Merry
1395672fac9SKenneth D. Merry STAILQ_INIT(&write_attr_list);
1405672fac9SKenneth D. Merry
1415672fac9SKenneth D. Merry /*
1425672fac9SKenneth D. Merry * By default, when displaying attribute values, we trim out
1435672fac9SKenneth D. Merry * non-ASCII characters in ASCII fields. We display all fields
1445672fac9SKenneth D. Merry * (description, attribute number, attribute size, and readonly
1455672fac9SKenneth D. Merry * status). We default to displaying raw text.
1465672fac9SKenneth D. Merry *
1475672fac9SKenneth D. Merry * XXX KDM need to port this to stable/10 and newer FreeBSD
1485672fac9SKenneth D. Merry * versions that have iconv built in and can convert codesets.
1495672fac9SKenneth D. Merry */
1505672fac9SKenneth D. Merry output_format = SCSI_ATTR_OUTPUT_NONASCII_TRIM |
1515672fac9SKenneth D. Merry SCSI_ATTR_OUTPUT_FIELD_ALL |
1525672fac9SKenneth D. Merry SCSI_ATTR_OUTPUT_TEXT_RAW;
1535672fac9SKenneth D. Merry
1545672fac9SKenneth D. Merry data_buf = malloc(dxfer_len);
1555672fac9SKenneth D. Merry if (data_buf == NULL) {
1565672fac9SKenneth D. Merry warn("%s: error allocating %u bytes", __func__, dxfer_len);
1575672fac9SKenneth D. Merry error = 1;
1585672fac9SKenneth D. Merry goto bailout;
1595672fac9SKenneth D. Merry }
1605672fac9SKenneth D. Merry
1615672fac9SKenneth D. Merry while ((c = getopt(argc, argv, combinedopt)) != -1) {
1625672fac9SKenneth D. Merry switch (c) {
1635672fac9SKenneth D. Merry case 'a':
1645672fac9SKenneth D. Merry attr_num = strtol(optarg, &endptr, 0);
1655672fac9SKenneth D. Merry if (*endptr != '\0') {
1665672fac9SKenneth D. Merry warnx("%s: invalid attribute number %s",
1675672fac9SKenneth D. Merry __func__, optarg);
1685672fac9SKenneth D. Merry error = 1;
1695672fac9SKenneth D. Merry goto bailout;
1705672fac9SKenneth D. Merry }
1715672fac9SKenneth D. Merry start_attr = attr_num;
1725672fac9SKenneth D. Merry break;
1735672fac9SKenneth D. Merry case 'c':
1745672fac9SKenneth D. Merry cached_attr = 1;
1755672fac9SKenneth D. Merry break;
1765672fac9SKenneth D. Merry case 'e':
1775672fac9SKenneth D. Merry element_address = strtol(optarg, &endptr, 0);
1785672fac9SKenneth D. Merry if (*endptr != '\0') {
1795672fac9SKenneth D. Merry warnx("%s: invalid element address %s",
1805672fac9SKenneth D. Merry __func__, optarg);
1815672fac9SKenneth D. Merry error = 1;
1825672fac9SKenneth D. Merry goto bailout;
1835672fac9SKenneth D. Merry }
1845672fac9SKenneth D. Merry break;
1855672fac9SKenneth D. Merry case 'F': {
1865672fac9SKenneth D. Merry scsi_nv_status status;
1875672fac9SKenneth D. Merry scsi_attrib_output_flags new_outflags;
1885672fac9SKenneth D. Merry int entry_num = 0;
1895672fac9SKenneth D. Merry char *tmpstr;
1905672fac9SKenneth D. Merry
1915672fac9SKenneth D. Merry if (isdigit(optarg[0])) {
1925672fac9SKenneth D. Merry output_format = strtoul(optarg, &endptr, 0);
1935672fac9SKenneth D. Merry if (*endptr != '\0') {
1945672fac9SKenneth D. Merry warnx("%s: invalid numeric output "
1955672fac9SKenneth D. Merry "format argument %s", __func__,
1965672fac9SKenneth D. Merry optarg);
1975672fac9SKenneth D. Merry error = 1;
1985672fac9SKenneth D. Merry goto bailout;
1995672fac9SKenneth D. Merry }
2005672fac9SKenneth D. Merry break;
2015672fac9SKenneth D. Merry }
2025672fac9SKenneth D. Merry new_outflags = SCSI_ATTR_OUTPUT_NONE;
2035672fac9SKenneth D. Merry
2045672fac9SKenneth D. Merry while ((tmpstr = strsep(&optarg, ",")) != NULL) {
2055672fac9SKenneth D. Merry status = scsi_get_nv(output_format_map,
2065672fac9SKenneth D. Merry sizeof(output_format_map) /
2075672fac9SKenneth D. Merry sizeof(output_format_map[0]), tmpstr,
2085672fac9SKenneth D. Merry &entry_num, SCSI_NV_FLAG_IG_CASE);
2095672fac9SKenneth D. Merry
2105672fac9SKenneth D. Merry if (status == SCSI_NV_FOUND)
2115672fac9SKenneth D. Merry new_outflags |=
2125672fac9SKenneth D. Merry output_format_map[entry_num].value;
2135672fac9SKenneth D. Merry else {
2145672fac9SKenneth D. Merry warnx("%s: %s format option %s",
2155672fac9SKenneth D. Merry __func__,
2165672fac9SKenneth D. Merry (status == SCSI_NV_AMBIGUOUS) ?
2175672fac9SKenneth D. Merry "ambiguous" : "invalid", tmpstr);
2185672fac9SKenneth D. Merry error = 1;
2195672fac9SKenneth D. Merry goto bailout;
2205672fac9SKenneth D. Merry }
2215672fac9SKenneth D. Merry }
2225672fac9SKenneth D. Merry output_format = new_outflags;
2235672fac9SKenneth D. Merry break;
2245672fac9SKenneth D. Merry }
2255672fac9SKenneth D. Merry case 'p':
2265672fac9SKenneth D. Merry partition = strtol(optarg, &endptr, 0);
2275672fac9SKenneth D. Merry if (*endptr != '\0') {
2285672fac9SKenneth D. Merry warnx("%s: invalid partition number %s",
2295672fac9SKenneth D. Merry __func__, optarg);
2305672fac9SKenneth D. Merry error = 1;
2315672fac9SKenneth D. Merry goto bailout;
2325672fac9SKenneth D. Merry }
2335672fac9SKenneth D. Merry break;
2345672fac9SKenneth D. Merry case 'r': {
2355672fac9SKenneth D. Merry scsi_nv_status status;
2365672fac9SKenneth D. Merry int entry_num = 0;
2375672fac9SKenneth D. Merry
2385672fac9SKenneth D. Merry status = scsi_get_nv(sa_map, sizeof(sa_map) /
2395672fac9SKenneth D. Merry sizeof(sa_map[0]), optarg, &entry_num,
2405672fac9SKenneth D. Merry SCSI_NV_FLAG_IG_CASE);
2415672fac9SKenneth D. Merry if (status == SCSI_NV_FOUND)
2425672fac9SKenneth D. Merry read_service_action = sa_map[entry_num].value;
2435672fac9SKenneth D. Merry else {
2445672fac9SKenneth D. Merry warnx("%s: %s %s option %s", __func__,
2455672fac9SKenneth D. Merry (status == SCSI_NV_AMBIGUOUS) ?
2465672fac9SKenneth D. Merry "ambiguous" : "invalid", "service action",
2475672fac9SKenneth D. Merry optarg);
2485672fac9SKenneth D. Merry error = 1;
2495672fac9SKenneth D. Merry goto bailout;
2505672fac9SKenneth D. Merry }
2515672fac9SKenneth D. Merry read_attr = 1;
2525672fac9SKenneth D. Merry break;
2535672fac9SKenneth D. Merry }
2545672fac9SKenneth D. Merry case 's':
2555672fac9SKenneth D. Merry start_attr = strtol(optarg, &endptr, 0);
2565672fac9SKenneth D. Merry if (*endptr != '\0') {
2575672fac9SKenneth D. Merry warnx("%s: invalid starting attr argument %s",
2585672fac9SKenneth D. Merry __func__, optarg);
2595672fac9SKenneth D. Merry error = 1;
2605672fac9SKenneth D. Merry goto bailout;
2615672fac9SKenneth D. Merry }
2625672fac9SKenneth D. Merry break;
2635672fac9SKenneth D. Merry case 'T': {
2645672fac9SKenneth D. Merry scsi_nv_status status;
2655672fac9SKenneth D. Merry int entry_num = 0;
2665672fac9SKenneth D. Merry
2675672fac9SKenneth D. Merry status = scsi_get_nv(elem_type_map,
268*7028e630SElyes Haouas nitems(elem_type_map),
2695672fac9SKenneth D. Merry optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
2705672fac9SKenneth D. Merry if (status == SCSI_NV_FOUND)
2715672fac9SKenneth D. Merry element_type = elem_type_map[entry_num].value;
2725672fac9SKenneth D. Merry else {
2735672fac9SKenneth D. Merry warnx("%s: %s %s option %s", __func__,
2745672fac9SKenneth D. Merry (status == SCSI_NV_AMBIGUOUS) ?
2755672fac9SKenneth D. Merry "ambiguous" : "invalid", "element type",
2765672fac9SKenneth D. Merry optarg);
2775672fac9SKenneth D. Merry error = 1;
2785672fac9SKenneth D. Merry goto bailout;
2795672fac9SKenneth D. Merry }
2805672fac9SKenneth D. Merry break;
2815672fac9SKenneth D. Merry }
2825672fac9SKenneth D. Merry case 'w':
2835672fac9SKenneth D. Merry warnx("%s: writing attributes is not implemented yet",
2845672fac9SKenneth D. Merry __func__);
2855672fac9SKenneth D. Merry error = 1;
2865672fac9SKenneth D. Merry goto bailout;
2875672fac9SKenneth D. Merry break;
2885672fac9SKenneth D. Merry case 'V':
2895672fac9SKenneth D. Merry logical_volume = strtol(optarg, &endptr, 0);
2905672fac9SKenneth D. Merry
2915672fac9SKenneth D. Merry if (*endptr != '\0') {
2925672fac9SKenneth D. Merry warnx("%s: invalid logical volume argument %s",
2935672fac9SKenneth D. Merry __func__, optarg);
2945672fac9SKenneth D. Merry error = 1;
2955672fac9SKenneth D. Merry goto bailout;
2965672fac9SKenneth D. Merry }
2975672fac9SKenneth D. Merry break;
2985672fac9SKenneth D. Merry default:
2995672fac9SKenneth D. Merry break;
3005672fac9SKenneth D. Merry }
3015672fac9SKenneth D. Merry }
3025672fac9SKenneth D. Merry
3035672fac9SKenneth D. Merry /*
3045672fac9SKenneth D. Merry * Default to reading attributes
3055672fac9SKenneth D. Merry */
3065672fac9SKenneth D. Merry if (((read_attr == 0) && (write_attr == 0))
3075672fac9SKenneth D. Merry || ((read_attr != 0) && (write_attr != 0))) {
3085672fac9SKenneth D. Merry warnx("%s: Must specify either -r or -w", __func__);
3095672fac9SKenneth D. Merry error = 1;
3105672fac9SKenneth D. Merry goto bailout;
3115672fac9SKenneth D. Merry }
3125672fac9SKenneth D. Merry
3135672fac9SKenneth D. Merry if (read_attr != 0) {
3145672fac9SKenneth D. Merry scsi_read_attribute(&ccb->csio,
3155672fac9SKenneth D. Merry /*retries*/ retry_count,
3165672fac9SKenneth D. Merry /*cbfcnp*/ NULL,
317492a2ef5SKenneth D. Merry /*tag_action*/ task_attr,
3185672fac9SKenneth D. Merry /*service_action*/ read_service_action,
3195672fac9SKenneth D. Merry /*element*/ element_address,
3205672fac9SKenneth D. Merry /*elem_type*/ element_type,
3215672fac9SKenneth D. Merry /*logical_volume*/ logical_volume,
3225672fac9SKenneth D. Merry /*partition*/ partition,
3235672fac9SKenneth D. Merry /*first_attribute*/ start_attr,
3245672fac9SKenneth D. Merry /*cache*/ cached_attr,
3255672fac9SKenneth D. Merry /*data_ptr*/ data_buf,
3265672fac9SKenneth D. Merry /*length*/ dxfer_len,
3275672fac9SKenneth D. Merry /*sense_len*/ SSD_FULL_SIZE,
3285672fac9SKenneth D. Merry /*timeout*/ timeout ? timeout : 60000);
3295672fac9SKenneth D. Merry #if 0
3305672fac9SKenneth D. Merry } else {
3315672fac9SKenneth D. Merry #endif
3325672fac9SKenneth D. Merry
3335672fac9SKenneth D. Merry }
3345672fac9SKenneth D. Merry
3355672fac9SKenneth D. Merry ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
3365672fac9SKenneth D. Merry
3375672fac9SKenneth D. Merry if (err_recover != 0)
3385672fac9SKenneth D. Merry ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
3395672fac9SKenneth D. Merry
3405672fac9SKenneth D. Merry if (cam_send_ccb(device, ccb) < 0) {
3415672fac9SKenneth D. Merry warn("error sending %s ATTRIBUTE", (read_attr != 0) ?
3425672fac9SKenneth D. Merry "READ" : "WRITE");
3435672fac9SKenneth D. Merry error = 1;
3445672fac9SKenneth D. Merry goto bailout;
3455672fac9SKenneth D. Merry }
3465672fac9SKenneth D. Merry
3475672fac9SKenneth D. Merry if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
3485672fac9SKenneth D. Merry if (verbosemode != 0) {
3495672fac9SKenneth D. Merry cam_error_print(device, ccb, CAM_ESF_ALL,
3505672fac9SKenneth D. Merry CAM_EPF_ALL, stderr);
3515672fac9SKenneth D. Merry }
3525672fac9SKenneth D. Merry error = 1;
3535672fac9SKenneth D. Merry goto bailout;
3545672fac9SKenneth D. Merry }
3555672fac9SKenneth D. Merry
3565672fac9SKenneth D. Merry if (read_attr == 0)
3575672fac9SKenneth D. Merry goto bailout;
3585672fac9SKenneth D. Merry
3595672fac9SKenneth D. Merry valid_len = dxfer_len - ccb->csio.resid;
3605672fac9SKenneth D. Merry
3615672fac9SKenneth D. Merry switch (read_service_action) {
3625672fac9SKenneth D. Merry case SRA_SA_ATTR_VALUES: {
3635672fac9SKenneth D. Merry uint32_t len_left, hdr_len, cur_len;
3645672fac9SKenneth D. Merry struct scsi_read_attribute_values *hdr;
3655672fac9SKenneth D. Merry struct scsi_mam_attribute_header *cur_id;
3665672fac9SKenneth D. Merry char error_str[512];
3675672fac9SKenneth D. Merry uint8_t *cur_pos;
3685672fac9SKenneth D. Merry struct sbuf *sb;
3695672fac9SKenneth D. Merry
3705672fac9SKenneth D. Merry hdr = (struct scsi_read_attribute_values *)data_buf;
3715672fac9SKenneth D. Merry
3725672fac9SKenneth D. Merry if (valid_len < sizeof(*hdr)) {
3735672fac9SKenneth D. Merry fprintf(stdout, "No attributes returned.\n");
3745672fac9SKenneth D. Merry error = 0;
3755672fac9SKenneth D. Merry goto bailout;
3765672fac9SKenneth D. Merry }
3775672fac9SKenneth D. Merry
3785672fac9SKenneth D. Merry sb = sbuf_new_auto();
3795672fac9SKenneth D. Merry if (sb == NULL) {
3805672fac9SKenneth D. Merry warn("%s: Unable to allocate sbuf", __func__);
3815672fac9SKenneth D. Merry error = 1;
3825672fac9SKenneth D. Merry goto bailout;
3835672fac9SKenneth D. Merry }
3845672fac9SKenneth D. Merry /*
3855672fac9SKenneth D. Merry * XXX KDM grab more data if it is available.
3865672fac9SKenneth D. Merry */
3875672fac9SKenneth D. Merry hdr_len = scsi_4btoul(hdr->length);
3885672fac9SKenneth D. Merry
3895672fac9SKenneth D. Merry for (len_left = MIN(valid_len, hdr_len),
3905672fac9SKenneth D. Merry cur_pos = &hdr->attribute_0[0]; len_left > sizeof(*cur_id);
3915672fac9SKenneth D. Merry len_left -= cur_len, cur_pos += cur_len) {
3925672fac9SKenneth D. Merry int cur_attr_num;
3935672fac9SKenneth D. Merry cur_id = (struct scsi_mam_attribute_header *)cur_pos;
3945672fac9SKenneth D. Merry cur_len = scsi_2btoul(cur_id->length) + sizeof(*cur_id);
3955672fac9SKenneth D. Merry cur_attr_num = scsi_2btoul(cur_id->id);
3965672fac9SKenneth D. Merry
3975672fac9SKenneth D. Merry if ((attr_num != -1)
3985672fac9SKenneth D. Merry && (cur_attr_num != attr_num))
3995672fac9SKenneth D. Merry continue;
4005672fac9SKenneth D. Merry
4015672fac9SKenneth D. Merry error = scsi_attrib_sbuf(sb, cur_id, len_left,
4025672fac9SKenneth D. Merry /*user_table*/ NULL, /*num_user_entries*/ 0,
4035672fac9SKenneth D. Merry /*prefer_user_table*/ 0, output_format, error_str,
4045672fac9SKenneth D. Merry sizeof(error_str));
4055672fac9SKenneth D. Merry if (error != 0) {
4065672fac9SKenneth D. Merry warnx("%s: %s", __func__, error_str);
4075672fac9SKenneth D. Merry sbuf_delete(sb);
4085672fac9SKenneth D. Merry error = 1;
4095672fac9SKenneth D. Merry goto bailout;
4105672fac9SKenneth D. Merry }
4115672fac9SKenneth D. Merry if (attr_num != -1)
4125672fac9SKenneth D. Merry break;
4135672fac9SKenneth D. Merry }
4145672fac9SKenneth D. Merry
4155672fac9SKenneth D. Merry sbuf_finish(sb);
4165672fac9SKenneth D. Merry fprintf(stdout, "%s", sbuf_data(sb));
4175672fac9SKenneth D. Merry sbuf_delete(sb);
4185672fac9SKenneth D. Merry break;
4195672fac9SKenneth D. Merry }
4205672fac9SKenneth D. Merry case SRA_SA_SUPPORTED_ATTRS:
4215672fac9SKenneth D. Merry case SRA_SA_ATTR_LIST: {
4225672fac9SKenneth D. Merry uint32_t len_left, hdr_len;
4235672fac9SKenneth D. Merry struct scsi_attrib_list_header *hdr;
4245672fac9SKenneth D. Merry struct scsi_attrib_table_entry *entry = NULL;
4255672fac9SKenneth D. Merry const char *sa_name = "Supported Attributes";
4265672fac9SKenneth D. Merry const char *at_name = "Available Attributes";
4275672fac9SKenneth D. Merry int attr_id;
4285672fac9SKenneth D. Merry uint8_t *cur_id;
4295672fac9SKenneth D. Merry
4305672fac9SKenneth D. Merry hdr = (struct scsi_attrib_list_header *)data_buf;
4315672fac9SKenneth D. Merry if (valid_len < sizeof(*hdr)) {
4325672fac9SKenneth D. Merry fprintf(stdout, "No %s\n",
4335672fac9SKenneth D. Merry (read_service_action == SRA_SA_SUPPORTED_ATTRS)?
4345672fac9SKenneth D. Merry sa_name : at_name);
4355672fac9SKenneth D. Merry error = 0;
4365672fac9SKenneth D. Merry goto bailout;
4375672fac9SKenneth D. Merry }
4385672fac9SKenneth D. Merry fprintf(stdout, "%s:\n",
4395672fac9SKenneth D. Merry (read_service_action == SRA_SA_SUPPORTED_ATTRS) ?
4405672fac9SKenneth D. Merry sa_name : at_name);
4415672fac9SKenneth D. Merry hdr_len = scsi_4btoul(hdr->length);
4425672fac9SKenneth D. Merry for (len_left = MIN(valid_len, hdr_len),
4435672fac9SKenneth D. Merry cur_id = &hdr->first_attr_0[0]; len_left > 1;
4445672fac9SKenneth D. Merry len_left -= sizeof(uint16_t), cur_id += sizeof(uint16_t)) {
4455672fac9SKenneth D. Merry attr_id = scsi_2btoul(cur_id);
4465672fac9SKenneth D. Merry
4475672fac9SKenneth D. Merry if ((attr_num != -1)
4485672fac9SKenneth D. Merry && (attr_id != attr_num))
4495672fac9SKenneth D. Merry continue;
4505672fac9SKenneth D. Merry
4515672fac9SKenneth D. Merry entry = scsi_get_attrib_entry(attr_id);
4525672fac9SKenneth D. Merry fprintf(stdout, "0x%.4x", attr_id);
4535672fac9SKenneth D. Merry if (entry == NULL)
4545672fac9SKenneth D. Merry fprintf(stdout, "\n");
4555672fac9SKenneth D. Merry else
4565672fac9SKenneth D. Merry fprintf(stdout, ": %s\n", entry->desc);
4575672fac9SKenneth D. Merry
4585672fac9SKenneth D. Merry if (attr_num != -1)
4595672fac9SKenneth D. Merry break;
4605672fac9SKenneth D. Merry }
4615672fac9SKenneth D. Merry break;
4625672fac9SKenneth D. Merry }
4635672fac9SKenneth D. Merry case SRA_SA_PART_LIST:
4645672fac9SKenneth D. Merry case SRA_SA_LOG_VOL_LIST: {
4655672fac9SKenneth D. Merry struct scsi_attrib_lv_list *lv_list;
4665672fac9SKenneth D. Merry const char *partition_name = "Partition";
4675672fac9SKenneth D. Merry const char *lv_name = "Logical Volume";
4685672fac9SKenneth D. Merry
4695672fac9SKenneth D. Merry if (valid_len < sizeof(*lv_list)) {
4705672fac9SKenneth D. Merry fprintf(stdout, "No %s list returned\n",
4715672fac9SKenneth D. Merry (read_service_action == SRA_SA_PART_LIST) ?
4725672fac9SKenneth D. Merry partition_name : lv_name);
4735672fac9SKenneth D. Merry error = 0;
4745672fac9SKenneth D. Merry goto bailout;
4755672fac9SKenneth D. Merry }
4765672fac9SKenneth D. Merry
4775672fac9SKenneth D. Merry lv_list = (struct scsi_attrib_lv_list *)data_buf;
4785672fac9SKenneth D. Merry
4795672fac9SKenneth D. Merry fprintf(stdout, "First %s: %d\n",
4805672fac9SKenneth D. Merry (read_service_action == SRA_SA_PART_LIST) ?
4815672fac9SKenneth D. Merry partition_name : lv_name,
4825672fac9SKenneth D. Merry lv_list->first_lv_number);
4835672fac9SKenneth D. Merry fprintf(stdout, "Number of %ss: %d\n",
4845672fac9SKenneth D. Merry (read_service_action == SRA_SA_PART_LIST) ?
4855672fac9SKenneth D. Merry partition_name : lv_name,
4865672fac9SKenneth D. Merry lv_list->num_logical_volumes);
4875672fac9SKenneth D. Merry break;
4885672fac9SKenneth D. Merry }
4895672fac9SKenneth D. Merry default:
4905672fac9SKenneth D. Merry break;
4915672fac9SKenneth D. Merry }
4925672fac9SKenneth D. Merry bailout:
4935672fac9SKenneth D. Merry if (ccb != NULL)
4945672fac9SKenneth D. Merry cam_freeccb(ccb);
4955672fac9SKenneth D. Merry
4965672fac9SKenneth D. Merry free(data_buf);
4975672fac9SKenneth D. Merry
4985672fac9SKenneth D. Merry return (error);
4995672fac9SKenneth D. Merry }
500