xref: /freebsd-src/sbin/camcontrol/attrib.c (revision 7028e630d6110789502cfc0f17b36b6f513ec297)
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