xref: /freebsd-src/sbin/camcontrol/zone.c (revision 7028e630d6110789502cfc0f17b36b6f513ec297)
19a6844d5SKenneth D. Merry /*-
29a6844d5SKenneth D. Merry  * Copyright (c) 2015, 2016 Spectra Logic Corporation
39a6844d5SKenneth D. Merry  * All rights reserved.
49a6844d5SKenneth D. Merry  *
59a6844d5SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
69a6844d5SKenneth D. Merry  * modification, are permitted provided that the following conditions
79a6844d5SKenneth D. Merry  * are met:
89a6844d5SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
99a6844d5SKenneth D. Merry  *    notice, this list of conditions, and the following disclaimer,
109a6844d5SKenneth D. Merry  *    without modification.
119a6844d5SKenneth D. Merry  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
129a6844d5SKenneth D. Merry  *    substantially similar to the "NO WARRANTY" disclaimer below
139a6844d5SKenneth D. Merry  *    ("Disclaimer") and any redistribution must be conditioned upon
149a6844d5SKenneth D. Merry  *    including a substantially similar Disclaimer requirement for further
159a6844d5SKenneth D. Merry  *    binary redistribution.
169a6844d5SKenneth D. Merry  *
179a6844d5SKenneth D. Merry  * NO WARRANTY
189a6844d5SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
199a6844d5SKenneth D. Merry  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
209a6844d5SKenneth D. Merry  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
219a6844d5SKenneth D. Merry  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
229a6844d5SKenneth D. Merry  * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
239a6844d5SKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
249a6844d5SKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
259a6844d5SKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
269a6844d5SKenneth D. Merry  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
279a6844d5SKenneth D. Merry  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
289a6844d5SKenneth D. Merry  * POSSIBILITY OF SUCH DAMAGES.
299a6844d5SKenneth D. Merry  *
309a6844d5SKenneth D. Merry  * Authors: Ken Merry           (Spectra Logic Corporation)
319a6844d5SKenneth D. Merry  */
329a6844d5SKenneth D. Merry /*
339a6844d5SKenneth D. Merry  * SCSI and ATA Shingled Media Recording (SMR) support for camcontrol(8).
349a6844d5SKenneth D. Merry  * This is an implementation of the SCSI ZBC and ATA ZAC specs.
359a6844d5SKenneth D. Merry  */
369a6844d5SKenneth D. Merry 
37*7028e630SElyes Haouas #include <sys/param.h>
389a6844d5SKenneth D. Merry #include <sys/ioctl.h>
399a6844d5SKenneth D. Merry #include <sys/stdint.h>
409a6844d5SKenneth D. Merry #include <sys/endian.h>
419a6844d5SKenneth D. Merry #include <sys/sbuf.h>
429a6844d5SKenneth D. Merry #include <sys/queue.h>
439a6844d5SKenneth D. Merry #include <sys/chio.h>
449a6844d5SKenneth D. Merry 
459a6844d5SKenneth D. Merry #include <stdio.h>
469a6844d5SKenneth D. Merry #include <stdlib.h>
479a6844d5SKenneth D. Merry #include <inttypes.h>
489a6844d5SKenneth D. Merry #include <unistd.h>
499a6844d5SKenneth D. Merry #include <string.h>
509a6844d5SKenneth D. Merry #include <strings.h>
519a6844d5SKenneth D. Merry #include <fcntl.h>
529a6844d5SKenneth D. Merry #include <ctype.h>
539a6844d5SKenneth D. Merry #include <limits.h>
549a6844d5SKenneth D. Merry #include <err.h>
559a6844d5SKenneth D. Merry #include <locale.h>
569a6844d5SKenneth D. Merry 
579a6844d5SKenneth D. Merry #include <cam/cam.h>
589a6844d5SKenneth D. Merry #include <cam/cam_debug.h>
599a6844d5SKenneth D. Merry #include <cam/cam_ccb.h>
609a6844d5SKenneth D. Merry #include <cam/scsi/scsi_all.h>
619a6844d5SKenneth D. Merry #include <cam/scsi/scsi_da.h>
629a6844d5SKenneth D. Merry #include <cam/scsi/scsi_pass.h>
639a6844d5SKenneth D. Merry #include <cam/scsi/scsi_ch.h>
649a6844d5SKenneth D. Merry #include <cam/scsi/scsi_message.h>
659a6844d5SKenneth D. Merry #include <camlib.h>
669a6844d5SKenneth D. Merry #include "camcontrol.h"
679a6844d5SKenneth D. Merry 
689a6844d5SKenneth D. Merry static struct scsi_nv zone_cmd_map[] = {
699a6844d5SKenneth D. Merry 	{ "rz", ZBC_IN_SA_REPORT_ZONES },
709a6844d5SKenneth D. Merry 	{ "reportzones", ZBC_IN_SA_REPORT_ZONES },
719a6844d5SKenneth D. Merry 	{ "close", ZBC_OUT_SA_CLOSE },
729a6844d5SKenneth D. Merry 	{ "finish", ZBC_OUT_SA_FINISH },
739a6844d5SKenneth D. Merry 	{ "open", ZBC_OUT_SA_OPEN },
749a6844d5SKenneth D. Merry 	{ "rwp", ZBC_OUT_SA_RWP }
759a6844d5SKenneth D. Merry };
769a6844d5SKenneth D. Merry 
779a6844d5SKenneth D. Merry static struct scsi_nv zone_rep_opts[] = {
789a6844d5SKenneth D. Merry 	{ "all", ZBC_IN_REP_ALL_ZONES },
799a6844d5SKenneth D. Merry 	{ "empty", ZBC_IN_REP_EMPTY },
809a6844d5SKenneth D. Merry 	{ "imp_open", ZBC_IN_REP_IMP_OPEN },
819a6844d5SKenneth D. Merry 	{ "exp_open", ZBC_IN_REP_EXP_OPEN },
829a6844d5SKenneth D. Merry 	{ "closed", ZBC_IN_REP_CLOSED },
839a6844d5SKenneth D. Merry 	{ "full", ZBC_IN_REP_FULL },
849a6844d5SKenneth D. Merry 	{ "readonly", ZBC_IN_REP_READONLY },
859a6844d5SKenneth D. Merry 	{ "ro", ZBC_IN_REP_READONLY },
869a6844d5SKenneth D. Merry 	{ "offline", ZBC_IN_REP_OFFLINE },
879a6844d5SKenneth D. Merry 	{ "rwp", ZBC_IN_REP_RESET },
889a6844d5SKenneth D. Merry 	{ "reset", ZBC_IN_REP_RESET },
899a6844d5SKenneth D. Merry 	{ "nonseq", ZBC_IN_REP_NON_SEQ },
909a6844d5SKenneth D. Merry 	{ "nonwp", ZBC_IN_REP_NON_WP }
919a6844d5SKenneth D. Merry };
929a6844d5SKenneth D. Merry 
939a6844d5SKenneth D. Merry typedef enum {
949a6844d5SKenneth D. Merry 	ZONE_OF_NORMAL	= 0x00,
959a6844d5SKenneth D. Merry 	ZONE_OF_SUMMARY	= 0x01,
969a6844d5SKenneth D. Merry 	ZONE_OF_SCRIPT	= 0x02
979a6844d5SKenneth D. Merry } zone_output_flags;
989a6844d5SKenneth D. Merry 
999a6844d5SKenneth D. Merry static struct scsi_nv zone_print_opts[] = {
1009a6844d5SKenneth D. Merry 	{ "normal", ZONE_OF_NORMAL },
1019a6844d5SKenneth D. Merry 	{ "summary", ZONE_OF_SUMMARY },
1029a6844d5SKenneth D. Merry 	{ "script", ZONE_OF_SCRIPT }
1039a6844d5SKenneth D. Merry };
1049a6844d5SKenneth D. Merry 
1059a6844d5SKenneth D. Merry #define	ZAC_ATA_SECTOR_COUNT(bcount)	(((bcount) / 512) & 0xffff)
1069a6844d5SKenneth D. Merry 
1079a6844d5SKenneth D. Merry typedef enum {
1089a6844d5SKenneth D. Merry 	ZONE_PRINT_OK,
1099a6844d5SKenneth D. Merry 	ZONE_PRINT_MORE_DATA,
1109a6844d5SKenneth D. Merry 	ZONE_PRINT_ERROR
1119a6844d5SKenneth D. Merry } zone_print_status;
1129a6844d5SKenneth D. Merry 
1139a6844d5SKenneth D. Merry typedef enum {
1149a6844d5SKenneth D. Merry 	ZONE_FW_START,
1159a6844d5SKenneth D. Merry 	ZONE_FW_LEN,
1169a6844d5SKenneth D. Merry 	ZONE_FW_WP,
1179a6844d5SKenneth D. Merry 	ZONE_FW_TYPE,
1189a6844d5SKenneth D. Merry 	ZONE_FW_COND,
1199a6844d5SKenneth D. Merry 	ZONE_FW_SEQ,
1209a6844d5SKenneth D. Merry 	ZONE_FW_RESET,
1219a6844d5SKenneth D. Merry 	ZONE_NUM_FIELDS
1229a6844d5SKenneth D. Merry } zone_field_widths;
1239a6844d5SKenneth D. Merry 
1249a6844d5SKenneth D. Merry zone_print_status zone_rz_print(uint8_t *data_ptr, uint32_t valid_len,
1259a6844d5SKenneth D. Merry 				int ata_format, zone_output_flags out_flags,
1269a6844d5SKenneth D. Merry 				int first_pass, uint64_t *next_start_lba);
1279a6844d5SKenneth D. Merry 
1289a6844d5SKenneth D. Merry 
1299a6844d5SKenneth D. Merry zone_print_status
zone_rz_print(uint8_t * data_ptr,uint32_t valid_len,int ata_format,zone_output_flags out_flags,int first_pass,uint64_t * next_start_lba)1309a6844d5SKenneth D. Merry zone_rz_print(uint8_t *data_ptr, uint32_t valid_len, int ata_format,
1319a6844d5SKenneth D. Merry 	      zone_output_flags out_flags, int first_pass,
1329a6844d5SKenneth D. Merry 	      uint64_t *next_start_lba)
1339a6844d5SKenneth D. Merry {
1349a6844d5SKenneth D. Merry 	struct scsi_report_zones_hdr *hdr = NULL;
1359a6844d5SKenneth D. Merry 	struct scsi_report_zones_desc *desc = NULL;
1369a6844d5SKenneth D. Merry 	uint32_t hdr_len, len;
1379a6844d5SKenneth D. Merry 	uint64_t max_lba, next_lba = 0;
1389a6844d5SKenneth D. Merry 	zone_print_status status = ZONE_PRINT_OK;
1399a6844d5SKenneth D. Merry 	char tmpstr[80];
1409a6844d5SKenneth D. Merry 	int field_widths[ZONE_NUM_FIELDS];
1419a6844d5SKenneth D. Merry 	char word_sep;
1429a6844d5SKenneth D. Merry 
1439a6844d5SKenneth D. Merry 	if (valid_len < sizeof(*hdr)) {
1449a6844d5SKenneth D. Merry 		status = ZONE_PRINT_ERROR;
1459a6844d5SKenneth D. Merry 		goto bailout;
1469a6844d5SKenneth D. Merry 	}
1479a6844d5SKenneth D. Merry 
1489a6844d5SKenneth D. Merry 	hdr = (struct scsi_report_zones_hdr *)data_ptr;
1499a6844d5SKenneth D. Merry 
1509a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_START] = 11;
1519a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_LEN] = 6;
1529a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_WP] = 11;
1539a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_TYPE] = 13;
1549a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_COND] = 13;
1559a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_SEQ] = 14;
1569a6844d5SKenneth D. Merry 	field_widths[ZONE_FW_RESET] = 16;
1579a6844d5SKenneth D. Merry 
1589a6844d5SKenneth D. Merry 	if (ata_format == 0) {
1599a6844d5SKenneth D. Merry 		hdr_len = scsi_4btoul(hdr->length);
1609a6844d5SKenneth D. Merry 		max_lba = scsi_8btou64(hdr->maximum_lba);
1619a6844d5SKenneth D. Merry 	} else {
1629a6844d5SKenneth D. Merry 		hdr_len = le32dec(hdr->length);
1639a6844d5SKenneth D. Merry 		max_lba = le64dec(hdr->maximum_lba);
1649a6844d5SKenneth D. Merry 	}
1659a6844d5SKenneth D. Merry 
1669a6844d5SKenneth D. Merry 	if (hdr_len > (valid_len + sizeof(*hdr))) {
1679a6844d5SKenneth D. Merry 		status = ZONE_PRINT_MORE_DATA;
1689a6844d5SKenneth D. Merry 	}
1699a6844d5SKenneth D. Merry 
1709a6844d5SKenneth D. Merry 	len = MIN(valid_len - sizeof(*hdr), hdr_len);
1719a6844d5SKenneth D. Merry 
1729a6844d5SKenneth D. Merry 	if (out_flags == ZONE_OF_SCRIPT)
1739a6844d5SKenneth D. Merry 		word_sep = '_';
1749a6844d5SKenneth D. Merry 	else
1759a6844d5SKenneth D. Merry 		word_sep = ' ';
1769a6844d5SKenneth D. Merry 
1779a6844d5SKenneth D. Merry 	if ((out_flags != ZONE_OF_SCRIPT)
1789a6844d5SKenneth D. Merry 	 && (first_pass != 0)) {
1799a6844d5SKenneth D. Merry 		printf("%zu zones, Maximum LBA %#jx (%ju)\n",
1809a6844d5SKenneth D. Merry 		    hdr_len / sizeof(*desc), (uintmax_t)max_lba,
1819a6844d5SKenneth D. Merry 		    (uintmax_t)max_lba);
1829a6844d5SKenneth D. Merry 
1839a6844d5SKenneth D. Merry 		switch (hdr->byte4 & SRZ_SAME_MASK) {
1849a6844d5SKenneth D. Merry 		case SRZ_SAME_ALL_DIFFERENT:
1859a6844d5SKenneth D. Merry 			printf("Zone lengths and types may vary\n");
1869a6844d5SKenneth D. Merry 			break;
1879a6844d5SKenneth D. Merry 		case SRZ_SAME_ALL_SAME:
1889a6844d5SKenneth D. Merry 			printf("Zone lengths and types are all the same\n");
1899a6844d5SKenneth D. Merry 			break;
1909a6844d5SKenneth D. Merry 		case SRZ_SAME_LAST_DIFFERENT:
1919a6844d5SKenneth D. Merry 			printf("Zone types are the same, last zone length "
1929a6844d5SKenneth D. Merry 			    "differs\n");
1939a6844d5SKenneth D. Merry 			break;
1949a6844d5SKenneth D. Merry 		case SRZ_SAME_TYPES_DIFFERENT:
1959a6844d5SKenneth D. Merry 			printf("Zone lengths are the same, types vary\n");
1969a6844d5SKenneth D. Merry 			break;
1979a6844d5SKenneth D. Merry 		default:
1989a6844d5SKenneth D. Merry 			printf("Unknown SAME field value %#x\n",
1999a6844d5SKenneth D. Merry 			    hdr->byte4 & SRZ_SAME_MASK);
2009a6844d5SKenneth D. Merry 			break;
2019a6844d5SKenneth D. Merry 		}
2029a6844d5SKenneth D. Merry 	}
2039a6844d5SKenneth D. Merry 	if (out_flags == ZONE_OF_SUMMARY) {
2049a6844d5SKenneth D. Merry 		status = ZONE_PRINT_OK;
2059a6844d5SKenneth D. Merry 		goto bailout;
2069a6844d5SKenneth D. Merry 	}
2079a6844d5SKenneth D. Merry 
2089a6844d5SKenneth D. Merry 	if ((out_flags == ZONE_OF_NORMAL)
2099a6844d5SKenneth D. Merry 	 && (first_pass != 0)) {
2109a6844d5SKenneth D. Merry 		printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
2119a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_START], "Start LBA",
2129a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_LEN], "Length",
2139a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_WP], "WP LBA",
2149a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_TYPE], "Zone Type",
2159a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_COND], "Condition",
2169a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_SEQ], "Sequential",
2179a6844d5SKenneth D. Merry 		    field_widths[ZONE_FW_RESET], "Reset");
2189a6844d5SKenneth D. Merry 	}
2199a6844d5SKenneth D. Merry 
2209a6844d5SKenneth D. Merry 	for (desc = &hdr->desc_list[0]; len >= sizeof(*desc);
2219a6844d5SKenneth D. Merry 	     len -= sizeof(*desc), desc++) {
2229a6844d5SKenneth D. Merry 		uint64_t length, start_lba, wp_lba;
2239a6844d5SKenneth D. Merry 
2249a6844d5SKenneth D. Merry 		if (ata_format == 0) {
2259a6844d5SKenneth D. Merry 			length = scsi_8btou64(desc->zone_length);
2269a6844d5SKenneth D. Merry 			start_lba = scsi_8btou64(desc->zone_start_lba);
2279a6844d5SKenneth D. Merry 			wp_lba = scsi_8btou64(desc->write_pointer_lba);
2289a6844d5SKenneth D. Merry 		} else {
2299a6844d5SKenneth D. Merry 			length = le64dec(desc->zone_length);
2309a6844d5SKenneth D. Merry 			start_lba = le64dec(desc->zone_start_lba);
2319a6844d5SKenneth D. Merry 			wp_lba = le64dec(desc->write_pointer_lba);
2329a6844d5SKenneth D. Merry 		}
2339a6844d5SKenneth D. Merry 
2349a6844d5SKenneth D. Merry 		printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
2359a6844d5SKenneth D. Merry 		    (uintmax_t)start_lba, field_widths[ZONE_FW_LEN],
2369a6844d5SKenneth D. Merry 		    (uintmax_t)length, field_widths[ZONE_FW_WP],
2379a6844d5SKenneth D. Merry 		    (uintmax_t)wp_lba);
2389a6844d5SKenneth D. Merry 
2399a6844d5SKenneth D. Merry 		switch (desc->zone_type & SRZ_TYPE_MASK) {
2409a6844d5SKenneth D. Merry 		case SRZ_TYPE_CONVENTIONAL:
2419a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Conventional");
2429a6844d5SKenneth D. Merry 			break;
2439a6844d5SKenneth D. Merry 		case SRZ_TYPE_SEQ_PREFERRED:
2449a6844d5SKenneth D. Merry 		case SRZ_TYPE_SEQ_REQUIRED:
2459a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
2469a6844d5SKenneth D. Merry 			    word_sep, ((desc->zone_type & SRZ_TYPE_MASK) ==
2479a6844d5SKenneth D. Merry 			    SRZ_TYPE_SEQ_PREFERRED) ? "Preferred" :
2489a6844d5SKenneth D. Merry 			    "Required");
2499a6844d5SKenneth D. Merry 			break;
2509a6844d5SKenneth D. Merry 		default:
2519a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
2529a6844d5SKenneth D. Merry 			    word_sep, word_sep,desc->zone_type &
2539a6844d5SKenneth D. Merry 			    SRZ_TYPE_MASK);
2549a6844d5SKenneth D. Merry 			break;
2559a6844d5SKenneth D. Merry 		}
2569a6844d5SKenneth D. Merry 		printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
2579a6844d5SKenneth D. Merry 
2589a6844d5SKenneth D. Merry 		switch (desc->zone_flags & SRZ_ZONE_COND_MASK) {
2599a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_NWP:
2609a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "NWP");
2619a6844d5SKenneth D. Merry 			break;
2629a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_EMPTY:
2639a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Empty");
2649a6844d5SKenneth D. Merry 			break;
2659a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_IMP_OPEN:
2669a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
2679a6844d5SKenneth D. Merry 			    word_sep);
2689a6844d5SKenneth D. Merry 			break;
2699a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_EXP_OPEN:
2709a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
2719a6844d5SKenneth D. Merry 			    word_sep);
2729a6844d5SKenneth D. Merry 			break;
2739a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_CLOSED:
2749a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Closed");
2759a6844d5SKenneth D. Merry 			break;
2769a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_READONLY:
2779a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Readonly");
2789a6844d5SKenneth D. Merry 			break;
2799a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_FULL:
2809a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Full");
2819a6844d5SKenneth D. Merry 			break;
2829a6844d5SKenneth D. Merry 		case SRZ_ZONE_COND_OFFLINE:
2839a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Offline");
2849a6844d5SKenneth D. Merry 			break;
2859a6844d5SKenneth D. Merry 		default:
2869a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "%#x",
2879a6844d5SKenneth D. Merry 			    desc->zone_flags & SRZ_ZONE_COND_MASK);
2889a6844d5SKenneth D. Merry 			break;
2899a6844d5SKenneth D. Merry 		}
2909a6844d5SKenneth D. Merry 
2919a6844d5SKenneth D. Merry 		printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
2929a6844d5SKenneth D. Merry 
2939a6844d5SKenneth D. Merry 		if (desc->zone_flags & SRZ_ZONE_NON_SEQ)
2949a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
2959a6844d5SKenneth D. Merry 			    word_sep);
2969a6844d5SKenneth D. Merry 		else
2979a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Sequential");
2989a6844d5SKenneth D. Merry 
2999a6844d5SKenneth D. Merry 		printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
3009a6844d5SKenneth D. Merry 
3019a6844d5SKenneth D. Merry 		if (desc->zone_flags & SRZ_ZONE_RESET)
3029a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
3039a6844d5SKenneth D. Merry 			    word_sep);
3049a6844d5SKenneth D. Merry 		else
3059a6844d5SKenneth D. Merry 			snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
3069a6844d5SKenneth D. Merry 			    word_sep, word_sep);
3079a6844d5SKenneth D. Merry 
3089a6844d5SKenneth D. Merry 		printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
3099a6844d5SKenneth D. Merry 
3109a6844d5SKenneth D. Merry 		next_lba = start_lba + length;
3119a6844d5SKenneth D. Merry 	}
3129a6844d5SKenneth D. Merry bailout:
3139a6844d5SKenneth D. Merry 	*next_start_lba = next_lba;
3149a6844d5SKenneth D. Merry 
3159a6844d5SKenneth D. Merry 	return (status);
3169a6844d5SKenneth D. Merry }
3179a6844d5SKenneth D. Merry 
3189a6844d5SKenneth D. Merry int
zone(struct cam_device * device,int argc,char ** argv,char * combinedopt,int task_attr,int retry_count,int timeout,int verbosemode __unused)3199a6844d5SKenneth D. Merry zone(struct cam_device *device, int argc, char **argv, char *combinedopt,
320492a2ef5SKenneth D. Merry      int task_attr, int retry_count, int timeout, int verbosemode __unused)
3219a6844d5SKenneth D. Merry {
3229a6844d5SKenneth D. Merry 	union ccb *ccb = NULL;
3239a6844d5SKenneth D. Merry 	int action = -1, rep_option = -1;
3249a6844d5SKenneth D. Merry 	int all_zones = 0;
3259a6844d5SKenneth D. Merry 	uint64_t lba = 0;
3269a6844d5SKenneth D. Merry 	int error = 0;
3279a6844d5SKenneth D. Merry 	uint8_t *data_ptr = NULL;
3289a6844d5SKenneth D. Merry 	uint32_t alloc_len = 65536, valid_len = 0;
3299a6844d5SKenneth D. Merry 	camcontrol_devtype devtype;
3309a6844d5SKenneth D. Merry 	int ata_format = 0, use_ncq = 0;
3319a6844d5SKenneth D. Merry 	int first_pass = 1;
3329a6844d5SKenneth D. Merry 	zone_print_status zp_status;
3339a6844d5SKenneth D. Merry 	zone_output_flags out_flags = ZONE_OF_NORMAL;
3349a6844d5SKenneth D. Merry 	uint8_t *cdb_storage = NULL;
3359a6844d5SKenneth D. Merry 	int cdb_storage_len = 32;
3369a6844d5SKenneth D. Merry 	int c;
3379a6844d5SKenneth D. Merry 
3389a6844d5SKenneth D. Merry 	ccb = cam_getccb(device);
3399a6844d5SKenneth D. Merry 	if (ccb == NULL) {
3409a6844d5SKenneth D. Merry 		warnx("%s: error allocating CCB", __func__);
3419a6844d5SKenneth D. Merry 		error = 1;
3429a6844d5SKenneth D. Merry 		goto bailout;
3439a6844d5SKenneth D. Merry 	}
3449a6844d5SKenneth D. Merry 
3459a6844d5SKenneth D. Merry 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
3469a6844d5SKenneth D. Merry 		switch (c) {
3479a6844d5SKenneth D. Merry 		case 'a':
3489a6844d5SKenneth D. Merry 			all_zones = 1;
3499a6844d5SKenneth D. Merry 			break;
3509a6844d5SKenneth D. Merry 		case 'c': {
3519a6844d5SKenneth D. Merry 			scsi_nv_status status;
3529a6844d5SKenneth D. Merry 			int entry_num;
3539a6844d5SKenneth D. Merry 
3549a6844d5SKenneth D. Merry 			status = scsi_get_nv(zone_cmd_map,
355*7028e630SElyes Haouas 			    nitems(zone_cmd_map),
3569a6844d5SKenneth D. Merry 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
3579a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
3589a6844d5SKenneth D. Merry 				action = zone_cmd_map[entry_num].value;
3599a6844d5SKenneth D. Merry 			else {
3609a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
3619a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
3629a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "zone command",
3639a6844d5SKenneth D. Merry 				    optarg);
3649a6844d5SKenneth D. Merry 				error = 1;
3659a6844d5SKenneth D. Merry 				goto bailout;
3669a6844d5SKenneth D. Merry 			}
3679a6844d5SKenneth D. Merry 			break;
3689a6844d5SKenneth D. Merry 		}
3699a6844d5SKenneth D. Merry 		case 'l': {
3709a6844d5SKenneth D. Merry 			char *endptr;
3719a6844d5SKenneth D. Merry 
3729a6844d5SKenneth D. Merry 			lba = strtoull(optarg, &endptr, 0);
3739a6844d5SKenneth D. Merry 			if (*endptr != '\0') {
3749a6844d5SKenneth D. Merry 				warnx("%s: invalid lba argument %s", __func__,
3759a6844d5SKenneth D. Merry 				    optarg);
3769a6844d5SKenneth D. Merry 				error = 1;
3779a6844d5SKenneth D. Merry 				goto bailout;
3789a6844d5SKenneth D. Merry 			}
3799a6844d5SKenneth D. Merry 			break;
3809a6844d5SKenneth D. Merry 		}
3819a6844d5SKenneth D. Merry 		case 'N':
3829a6844d5SKenneth D. Merry 			use_ncq = 1;
3839a6844d5SKenneth D. Merry 			break;
3849a6844d5SKenneth D. Merry 		case 'o': {
3859a6844d5SKenneth D. Merry 			scsi_nv_status status;
3869a6844d5SKenneth D. Merry 			int entry_num;
3879a6844d5SKenneth D. Merry 
3889a6844d5SKenneth D. Merry 			status = scsi_get_nv(zone_rep_opts,
389*7028e630SElyes Haouas 			    nitems(zone_rep_opts),
3909a6844d5SKenneth D. Merry 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
3919a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
3929a6844d5SKenneth D. Merry 				rep_option = zone_rep_opts[entry_num].value;
3939a6844d5SKenneth D. Merry 			else {
3949a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
3959a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
3969a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "report zones",
3979a6844d5SKenneth D. Merry 				    optarg);
3989a6844d5SKenneth D. Merry 				error = 1;
3999a6844d5SKenneth D. Merry 				goto bailout;
4009a6844d5SKenneth D. Merry 			}
4019a6844d5SKenneth D. Merry 			break;
4029a6844d5SKenneth D. Merry 		}
4039a6844d5SKenneth D. Merry 		case 'P': {
4049a6844d5SKenneth D. Merry 			scsi_nv_status status;
4059a6844d5SKenneth D. Merry 			int entry_num;
4069a6844d5SKenneth D. Merry 
4079a6844d5SKenneth D. Merry 			status = scsi_get_nv(zone_print_opts,
4089a6844d5SKenneth D. Merry 			    (sizeof(zone_print_opts) /
4099a6844d5SKenneth D. Merry 			    sizeof(zone_print_opts[0])), optarg, &entry_num,
4109a6844d5SKenneth D. Merry 			    SCSI_NV_FLAG_IG_CASE);
4119a6844d5SKenneth D. Merry 			if (status == SCSI_NV_FOUND)
4129a6844d5SKenneth D. Merry 				out_flags = zone_print_opts[entry_num].value;
4139a6844d5SKenneth D. Merry 			else {
4149a6844d5SKenneth D. Merry 				warnx("%s: %s: %s option %s", __func__,
4159a6844d5SKenneth D. Merry 				    (status == SCSI_NV_AMBIGUOUS) ?
4169a6844d5SKenneth D. Merry 				    "ambiguous" : "invalid", "print",
4179a6844d5SKenneth D. Merry 				    optarg);
4189a6844d5SKenneth D. Merry 				error = 1;
4199a6844d5SKenneth D. Merry 				goto bailout;
4209a6844d5SKenneth D. Merry 			}
4219a6844d5SKenneth D. Merry 			break;
4229a6844d5SKenneth D. Merry 		}
4239a6844d5SKenneth D. Merry 		default:
4249a6844d5SKenneth D. Merry 			break;
4259a6844d5SKenneth D. Merry 		}
4269a6844d5SKenneth D. Merry 	}
4279a6844d5SKenneth D. Merry 	if (action == -1) {
4289a6844d5SKenneth D. Merry 		warnx("%s: must specify -c <zone_cmd>", __func__);
4299a6844d5SKenneth D. Merry 		error = 1;
4309a6844d5SKenneth D. Merry 		goto bailout;
4319a6844d5SKenneth D. Merry 	}
4329a6844d5SKenneth D. Merry 	error = get_device_type(device, retry_count, timeout,
4339a6844d5SKenneth D. Merry 	    /*printerrors*/ 1, &devtype);
4349a6844d5SKenneth D. Merry 	if (error != 0)
4359a6844d5SKenneth D. Merry 		errx(1, "Unable to determine device type");
4369a6844d5SKenneth D. Merry 
4379a6844d5SKenneth D. Merry 	if (action == ZBC_IN_SA_REPORT_ZONES) {
4389a6844d5SKenneth D. Merry 
4399a6844d5SKenneth D. Merry 		data_ptr = malloc(alloc_len);
4409a6844d5SKenneth D. Merry 		if (data_ptr == NULL)
4419a6844d5SKenneth D. Merry 			err(1, "unable to allocate %u bytes", alloc_len);
4429a6844d5SKenneth D. Merry 
4439a6844d5SKenneth D. Merry restart_report:
4449a6844d5SKenneth D. Merry 		bzero(data_ptr, alloc_len);
4459a6844d5SKenneth D. Merry 
4469a6844d5SKenneth D. Merry 		switch (devtype) {
4479a6844d5SKenneth D. Merry 		case CC_DT_SCSI:
4489a6844d5SKenneth D. Merry 			scsi_zbc_in(&ccb->csio,
4499a6844d5SKenneth D. Merry 			    /*retries*/ retry_count,
4509a6844d5SKenneth D. Merry 			    /*cbfcnp*/ NULL,
451492a2ef5SKenneth D. Merry 			    /*tag_action*/ task_attr,
4529a6844d5SKenneth D. Merry 			    /*service_action*/ action,
4539a6844d5SKenneth D. Merry 			    /*zone_start_lba*/ lba,
4549a6844d5SKenneth D. Merry 			    /*zone_options*/ (rep_option != -1) ?
4559a6844d5SKenneth D. Merry 					      rep_option : 0,
4569a6844d5SKenneth D. Merry 			    /*data_ptr*/ data_ptr,
4579a6844d5SKenneth D. Merry 			    /*dxfer_len*/ alloc_len,
4589a6844d5SKenneth D. Merry 			    /*sense_len*/ SSD_FULL_SIZE,
4599a6844d5SKenneth D. Merry 			    /*timeout*/ timeout ? timeout : 60000);
4609a6844d5SKenneth D. Merry 			break;
4619a6844d5SKenneth D. Merry 		case CC_DT_ATA:
46240152db5SWarner Losh 		case CC_DT_SATL: {
4639a6844d5SKenneth D. Merry 			uint8_t command = 0;
4649a6844d5SKenneth D. Merry 			uint8_t protocol = 0;
4659a6844d5SKenneth D. Merry 			uint16_t features = 0, sector_count = 0;
4669a6844d5SKenneth D. Merry 			uint32_t auxiliary = 0;
4679a6844d5SKenneth D. Merry 
4689a6844d5SKenneth D. Merry 			/*
4699a6844d5SKenneth D. Merry 			 * XXX KDM support the partial bit?
4709a6844d5SKenneth D. Merry 			 */
4719a6844d5SKenneth D. Merry 			if (use_ncq == 0) {
4729a6844d5SKenneth D. Merry 				command = ATA_ZAC_MANAGEMENT_IN;
4739a6844d5SKenneth D. Merry 				features = action;
4749a6844d5SKenneth D. Merry 				if (rep_option != -1)
4759a6844d5SKenneth D. Merry 					features |= (rep_option << 8);
4769a6844d5SKenneth D. Merry 				sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len);
4779a6844d5SKenneth D. Merry 				protocol = AP_PROTO_DMA;
4789a6844d5SKenneth D. Merry 			} else {
4794992a192SDon Lewis 				if (cdb_storage == NULL)
4809a6844d5SKenneth D. Merry 					cdb_storage = calloc(cdb_storage_len, 1);
4819a6844d5SKenneth D. Merry 				if (cdb_storage == NULL)
4829a6844d5SKenneth D. Merry 					err(1, "couldn't allocate memory");
4839a6844d5SKenneth D. Merry 
4849a6844d5SKenneth D. Merry 				command = ATA_RECV_FPDMA_QUEUED;
4859a6844d5SKenneth D. Merry 				features = ZAC_ATA_SECTOR_COUNT(alloc_len);
4869a6844d5SKenneth D. Merry 				sector_count = ATA_RFPDMA_ZAC_MGMT_IN << 8;
4879a6844d5SKenneth D. Merry 				auxiliary = action & 0xf;
4889a6844d5SKenneth D. Merry 				if (rep_option != -1)
4899a6844d5SKenneth D. Merry 					auxiliary |= rep_option << 8;
4909a6844d5SKenneth D. Merry 				protocol = AP_PROTO_FPDMA;
4919a6844d5SKenneth D. Merry 			}
4929a6844d5SKenneth D. Merry 
4939a6844d5SKenneth D. Merry 			error = build_ata_cmd(ccb,
4949a6844d5SKenneth D. Merry 			    /*retry_count*/ retry_count,
4959a6844d5SKenneth D. Merry 			    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
496492a2ef5SKenneth D. Merry 			    /*tag_action*/ task_attr,
4979a6844d5SKenneth D. Merry 			    /*protocol*/ protocol,
4989a6844d5SKenneth D. Merry 			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
4999a6844d5SKenneth D. Merry 					  AP_FLAG_TLEN_SECT_CNT |
5009a6844d5SKenneth D. Merry 					  AP_FLAG_TDIR_FROM_DEV,
5019a6844d5SKenneth D. Merry 			    /*features*/ features,
5029a6844d5SKenneth D. Merry 			    /*sector_count*/ sector_count,
5039a6844d5SKenneth D. Merry 			    /*lba*/ lba,
5049a6844d5SKenneth D. Merry 			    /*command*/ command,
5059a6844d5SKenneth D. Merry 			    /*auxiliary*/ auxiliary,
5069a6844d5SKenneth D. Merry 			    /*data_ptr*/ data_ptr,
5079a6844d5SKenneth D. Merry 			    /*dxfer_len*/ ZAC_ATA_SECTOR_COUNT(alloc_len)*512,
5089a6844d5SKenneth D. Merry 			    /*cdb_storage*/ cdb_storage,
5099a6844d5SKenneth D. Merry 			    /*cdb_storage_len*/ cdb_storage_len,
5109a6844d5SKenneth D. Merry 			    /*sense_len*/ SSD_FULL_SIZE,
5119a6844d5SKenneth D. Merry 			    /*timeout*/ timeout ? timeout : 60000,
5129a6844d5SKenneth D. Merry 			    /*is48bit*/ 1,
5139a6844d5SKenneth D. Merry 			    /*devtype*/ devtype);
5149a6844d5SKenneth D. Merry 
5159a6844d5SKenneth D. Merry 			if (error != 0) {
5169a6844d5SKenneth D. Merry 				warnx("%s: build_ata_cmd() failed, likely "
5179a6844d5SKenneth D. Merry 				    "programmer error", __func__);
5189a6844d5SKenneth D. Merry 				goto bailout;
5199a6844d5SKenneth D. Merry 			}
5209a6844d5SKenneth D. Merry 
5219a6844d5SKenneth D. Merry 			ata_format = 1;
5229a6844d5SKenneth D. Merry 
5239a6844d5SKenneth D. Merry 			break;
5249a6844d5SKenneth D. Merry 		}
5259a6844d5SKenneth D. Merry 		default:
5269a6844d5SKenneth D. Merry 			warnx("%s: Unknown device type %d", __func__,devtype);
5279a6844d5SKenneth D. Merry 			error = 1;
5289a6844d5SKenneth D. Merry 			goto bailout;
5299a6844d5SKenneth D. Merry 			break; /*NOTREACHED*/
5309a6844d5SKenneth D. Merry 		}
5319a6844d5SKenneth D. Merry 	} else {
5329a6844d5SKenneth D. Merry 		/*
5339a6844d5SKenneth D. Merry 		 * XXX KDM the current methodology is to always send ATA
5349a6844d5SKenneth D. Merry 		 * commands to ATA devices.  Need to figure out how to
5359a6844d5SKenneth D. Merry 		 * detect whether a SCSI to ATA translation layer will
5369a6844d5SKenneth D. Merry 		 * translate ZBC IN/OUT commands to the appropriate ZAC
5379a6844d5SKenneth D. Merry 		 * command.
5389a6844d5SKenneth D. Merry 		 */
5399a6844d5SKenneth D. Merry 		switch (devtype) {
5409a6844d5SKenneth D. Merry 		case CC_DT_SCSI:
5419a6844d5SKenneth D. Merry 			scsi_zbc_out(&ccb->csio,
5429a6844d5SKenneth D. Merry 			    /*retries*/ retry_count,
5439a6844d5SKenneth D. Merry 			    /*cbfcnp*/ NULL,
544492a2ef5SKenneth D. Merry 			    /*tag_action*/ task_attr,
5459a6844d5SKenneth D. Merry 			    /*service_action*/ action,
5469a6844d5SKenneth D. Merry 			    /*zone_id*/ lba,
5479a6844d5SKenneth D. Merry 			    /*zone_flags*/ (all_zones != 0) ? ZBC_OUT_ALL : 0,
5489a6844d5SKenneth D. Merry 			    /*data_ptr*/ NULL,
5499a6844d5SKenneth D. Merry 			    /*dxfer_len*/ 0,
5509a6844d5SKenneth D. Merry 			    /*sense_len*/ SSD_FULL_SIZE,
5519a6844d5SKenneth D. Merry 			    /*timeout*/ timeout ? timeout : 60000);
5529a6844d5SKenneth D. Merry 			break;
5539a6844d5SKenneth D. Merry 		case CC_DT_ATA:
55440152db5SWarner Losh 		case CC_DT_SATL: {
5559a6844d5SKenneth D. Merry 			uint8_t command = 0;
5569a6844d5SKenneth D. Merry 			uint8_t protocol = 0;
5579a6844d5SKenneth D. Merry 			uint16_t features = 0, sector_count = 0;
5589a6844d5SKenneth D. Merry 			uint32_t auxiliary = 0;
5599a6844d5SKenneth D. Merry 
5609a6844d5SKenneth D. Merry 			/*
5619a6844d5SKenneth D. Merry 			 * Note that we're taking advantage of the fact
5629a6844d5SKenneth D. Merry 			 * that the action numbers are the same between the
5639a6844d5SKenneth D. Merry 			 * ZBC and ZAC specs.
5649a6844d5SKenneth D. Merry 			 */
5659a6844d5SKenneth D. Merry 
5669a6844d5SKenneth D. Merry 			if (use_ncq == 0) {
5679a6844d5SKenneth D. Merry 				protocol = AP_PROTO_NON_DATA;
5689a6844d5SKenneth D. Merry 				command = ATA_ZAC_MANAGEMENT_OUT;
5699a6844d5SKenneth D. Merry 				features = action & 0xf;
5709a6844d5SKenneth D. Merry 				if (all_zones != 0)
5719a6844d5SKenneth D. Merry 					features |= (ZBC_OUT_ALL << 8);
5729a6844d5SKenneth D. Merry 			} else {
5739a6844d5SKenneth D. Merry 				cdb_storage = calloc(cdb_storage_len, 1);
5749a6844d5SKenneth D. Merry 				if (cdb_storage == NULL)
5759a6844d5SKenneth D. Merry 					err(1, "couldn't allocate memory");
5769a6844d5SKenneth D. Merry 
5779a6844d5SKenneth D. Merry 				protocol = AP_PROTO_FPDMA;
5789a6844d5SKenneth D. Merry 				command = ATA_NCQ_NON_DATA;
5799a6844d5SKenneth D. Merry 				features = ATA_NCQ_ZAC_MGMT_OUT;
5809a6844d5SKenneth D. Merry 				auxiliary = action & 0xf;
5819a6844d5SKenneth D. Merry 				if (all_zones != 0)
5829a6844d5SKenneth D. Merry 					auxiliary |= (ZBC_OUT_ALL << 8);
5839a6844d5SKenneth D. Merry 			}
5849a6844d5SKenneth D. Merry 
5859a6844d5SKenneth D. Merry 
5869a6844d5SKenneth D. Merry 			error = build_ata_cmd(ccb,
5879a6844d5SKenneth D. Merry 			    /*retry_count*/ retry_count,
5889a6844d5SKenneth D. Merry 			    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
589492a2ef5SKenneth D. Merry 			    /*tag_action*/ task_attr,
59090f05f68SWarner Losh 			    /*protocol*/ protocol,
5919a6844d5SKenneth D. Merry 			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
5929a6844d5SKenneth D. Merry 					  AP_FLAG_TLEN_NO_DATA,
5939a6844d5SKenneth D. Merry 			    /*features*/ features,
5949a6844d5SKenneth D. Merry 			    /*sector_count*/ sector_count,
5959a6844d5SKenneth D. Merry 			    /*lba*/ lba,
5969a6844d5SKenneth D. Merry 			    /*command*/ command,
5979a6844d5SKenneth D. Merry 			    /*auxiliary*/ auxiliary,
5989a6844d5SKenneth D. Merry 			    /*data_ptr*/ NULL,
5999a6844d5SKenneth D. Merry 			    /*dxfer_len*/ 0,
6009a6844d5SKenneth D. Merry 			    /*cdb_storage*/ cdb_storage,
6019a6844d5SKenneth D. Merry 			    /*cdb_storage_len*/ cdb_storage_len,
6029a6844d5SKenneth D. Merry 			    /*sense_len*/ SSD_FULL_SIZE,
6039a6844d5SKenneth D. Merry 			    /*timeout*/ timeout ? timeout : 60000,
6049a6844d5SKenneth D. Merry 			    /*is48bit*/ 1,
6059a6844d5SKenneth D. Merry 			    /*devtype*/ devtype);
6069a6844d5SKenneth D. Merry 			if (error != 0) {
6079a6844d5SKenneth D. Merry 				warnx("%s: build_ata_cmd() failed, likely "
6089a6844d5SKenneth D. Merry 				    "programmer error", __func__);
6099a6844d5SKenneth D. Merry 				goto bailout;
6109a6844d5SKenneth D. Merry 			}
6119a6844d5SKenneth D. Merry 			ata_format = 1;
6129a6844d5SKenneth D. Merry 			break;
6139a6844d5SKenneth D. Merry 		}
6149a6844d5SKenneth D. Merry 		default:
6159a6844d5SKenneth D. Merry 			warnx("%s: Unknown device type %d", __func__,devtype);
6169a6844d5SKenneth D. Merry 			error = 1;
6179a6844d5SKenneth D. Merry 			goto bailout;
6189a6844d5SKenneth D. Merry 			break; /*NOTREACHED*/
6199a6844d5SKenneth D. Merry 		}
6209a6844d5SKenneth D. Merry 	}
6219a6844d5SKenneth D. Merry 
6229a6844d5SKenneth D. Merry 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
6239a6844d5SKenneth D. Merry 	if (retry_count > 0)
6249a6844d5SKenneth D. Merry 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
6259a6844d5SKenneth D. Merry 
6269a6844d5SKenneth D. Merry 	error = cam_send_ccb(device, ccb);
6279a6844d5SKenneth D. Merry 	if (error != 0) {
6289a6844d5SKenneth D. Merry 		warn("error sending %s %s CCB", (devtype == CC_DT_SCSI) ?
6299a6844d5SKenneth D. Merry 		     "ZBC" : "ZAC Management",
6309a6844d5SKenneth D. Merry 		     (action == ZBC_IN_SA_REPORT_ZONES) ? "In" : "Out");
6319a6844d5SKenneth D. Merry 		error = -1;
6329a6844d5SKenneth D. Merry 		goto bailout;
6339a6844d5SKenneth D. Merry 	}
6349a6844d5SKenneth D. Merry 
6359a6844d5SKenneth D. Merry 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
6369a6844d5SKenneth D. Merry 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
6379a6844d5SKenneth D. Merry 		error = 1;
6389a6844d5SKenneth D. Merry 		goto bailout;
6399a6844d5SKenneth D. Merry 	}
6409a6844d5SKenneth D. Merry 
6419a6844d5SKenneth D. Merry 	/*
6429a6844d5SKenneth D. Merry 	 * If we aren't reading the list of zones, we're done.
6439a6844d5SKenneth D. Merry 	 */
6449a6844d5SKenneth D. Merry 	if (action != ZBC_IN_SA_REPORT_ZONES)
6459a6844d5SKenneth D. Merry 		goto bailout;
6469a6844d5SKenneth D. Merry 
6479a6844d5SKenneth D. Merry 	if (ccb->ccb_h.func_code == XPT_SCSI_IO)
6489a6844d5SKenneth D. Merry 		valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
6499a6844d5SKenneth D. Merry 	else
6509a6844d5SKenneth D. Merry 		valid_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
6519a6844d5SKenneth D. Merry 
6529a6844d5SKenneth D. Merry 	zp_status = zone_rz_print(data_ptr, valid_len, ata_format, out_flags,
6539a6844d5SKenneth D. Merry 	    first_pass, &lba);
6549a6844d5SKenneth D. Merry 
6559a6844d5SKenneth D. Merry 	if (zp_status == ZONE_PRINT_MORE_DATA) {
6569a6844d5SKenneth D. Merry 		bzero(ccb, sizeof(*ccb));
6579a6844d5SKenneth D. Merry 		first_pass = 0;
6584992a192SDon Lewis 		if (cdb_storage != NULL)
6594992a192SDon Lewis 			bzero(cdb_storage, cdb_storage_len);
6609a6844d5SKenneth D. Merry 		goto restart_report;
6619a6844d5SKenneth D. Merry 	} else if (zp_status == ZONE_PRINT_ERROR)
6629a6844d5SKenneth D. Merry 		error = 1;
6639a6844d5SKenneth D. Merry bailout:
6649a6844d5SKenneth D. Merry 	if (ccb != NULL)
6659a6844d5SKenneth D. Merry 		cam_freeccb(ccb);
6669a6844d5SKenneth D. Merry 
6679a6844d5SKenneth D. Merry 	free(data_ptr);
6689a6844d5SKenneth D. Merry 	free(cdb_storage);
6699a6844d5SKenneth D. Merry 
6709a6844d5SKenneth D. Merry 	return (error);
6719a6844d5SKenneth D. Merry }
672