zone.c revision 314220
1168457Skato/*-
264123Skato * Copyright (c) 2015, 2016 Spectra Logic Corporation
364123Skato * All rights reserved.
464123Skato *
564123Skato * Redistribution and use in source and binary forms, with or without
664123Skato * modification, are permitted provided that the following conditions
764123Skato * are met:
864123Skato * 1. Redistributions of source code must retain the above copyright
964123Skato *    notice, this list of conditions, and the following disclaimer,
1064123Skato *    without modification.
1164123Skato * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1264123Skato *    substantially similar to the "NO WARRANTY" disclaimer below
1364123Skato *    ("Disclaimer") and any redistribution must be conditioned upon
1464123Skato *    including a substantially similar Disclaimer requirement for further
1564123Skato *    binary redistribution.
1664123Skato *
1764123Skato * NO WARRANTY
1864123Skato * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1964123Skato * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2064123Skato * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
2164123Skato * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2264123Skato * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2364123Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2464123Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2564123Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2664123Skato * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
2764123Skato * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2864123Skato * POSSIBILITY OF SUCH DAMAGES.
2964123Skato *
3064123Skato * Authors: Ken Merry           (Spectra Logic Corporation)
3164123Skato */
3264123Skato/*
3364123Skato * SCSI and ATA Shingled Media Recording (SMR) support for camcontrol(8).
3464123Skato * This is an implementation of the SCSI ZBC and ATA ZAC specs.
3564123Skato */
3664123Skato
3764123Skato#include <sys/cdefs.h>
38201399Smbr__FBSDID("$FreeBSD: stable/11/sbin/camcontrol/zone.c 314220 2017-02-24 20:47:56Z ken $");
3964123Skato
4064123Skato#include <sys/ioctl.h>
4164123Skato#include <sys/stdint.h>
4264123Skato#include <sys/types.h>
4364123Skato#include <sys/endian.h>
4464123Skato#include <sys/sbuf.h>
4564123Skato#include <sys/queue.h>
4664123Skato#include <sys/chio.h>
4764123Skato
4864123Skato#include <stdio.h>
4964123Skato#include <stdlib.h>
5064123Skato#include <inttypes.h>
5164123Skato#include <unistd.h>
5264123Skato#include <string.h>
5364123Skato#include <strings.h>
5464123Skato#include <fcntl.h>
5564123Skato#include <ctype.h>
5664123Skato#include <limits.h>
5764123Skato#include <err.h>
5864123Skato#include <locale.h>
5964123Skato
6064123Skato#include <cam/cam.h>
6164123Skato#include <cam/cam_debug.h>
6264123Skato#include <cam/cam_ccb.h>
6364123Skato#include <cam/scsi/scsi_all.h>
6464123Skato#include <cam/scsi/scsi_da.h>
6564123Skato#include <cam/scsi/scsi_pass.h>
6664123Skato#include <cam/scsi/scsi_ch.h>
6764123Skato#include <cam/scsi/scsi_message.h>
6864123Skato#include <camlib.h>
6964123Skato#include "camcontrol.h"
7064123Skato
7164123Skatostatic struct scsi_nv zone_cmd_map[] = {
7264123Skato	{ "rz", ZBC_IN_SA_REPORT_ZONES },
7364123Skato	{ "reportzones", ZBC_IN_SA_REPORT_ZONES },
7464123Skato	{ "close", ZBC_OUT_SA_CLOSE },
7564123Skato	{ "finish", ZBC_OUT_SA_FINISH },
7664123Skato	{ "open", ZBC_OUT_SA_OPEN },
7764123Skato	{ "rwp", ZBC_OUT_SA_RWP }
7864123Skato};
7964123Skato
8064123Skatostatic struct scsi_nv zone_rep_opts[] = {
8164123Skato	{ "all", ZBC_IN_REP_ALL_ZONES },
8264123Skato	{ "empty", ZBC_IN_REP_EMPTY },
8364123Skato	{ "imp_open", ZBC_IN_REP_IMP_OPEN },
8464123Skato	{ "exp_open", ZBC_IN_REP_EXP_OPEN },
8564123Skato	{ "closed", ZBC_IN_REP_CLOSED },
8664123Skato	{ "full", ZBC_IN_REP_FULL },
8764123Skato	{ "readonly", ZBC_IN_REP_READONLY },
8864123Skato	{ "ro", ZBC_IN_REP_READONLY },
8964123Skato	{ "offline", ZBC_IN_REP_OFFLINE },
9064123Skato	{ "rwp", ZBC_IN_REP_RESET },
9164123Skato	{ "reset", ZBC_IN_REP_RESET },
9264123Skato	{ "nonseq", ZBC_IN_REP_NON_SEQ },
9364123Skato	{ "nonwp", ZBC_IN_REP_NON_WP }
9464123Skato};
9564123Skato
9664123Skatotypedef enum {
9764123Skato	ZONE_OF_NORMAL	= 0x00,
9864123Skato	ZONE_OF_SUMMARY	= 0x01,
9964123Skato	ZONE_OF_SCRIPT	= 0x02
10064123Skato} zone_output_flags;
10164123Skato
10264123Skatostatic struct scsi_nv zone_print_opts[] = {
10364123Skato	{ "normal", ZONE_OF_NORMAL },
10464123Skato	{ "summary", ZONE_OF_SUMMARY },
10564123Skato	{ "script", ZONE_OF_SCRIPT }
10664123Skato};
10764123Skato
10864123Skato#define	ZAC_ATA_SECTOR_COUNT(bcount)	(((bcount) / 512) & 0xffff)
10964123Skato
11064123Skatotypedef enum {
11164123Skato	ZONE_PRINT_OK,
11264123Skato	ZONE_PRINT_MORE_DATA,
11364123Skato	ZONE_PRINT_ERROR
11464123Skato} zone_print_status;
11564123Skato
11664123Skatotypedef enum {
11764123Skato	ZONE_FW_START,
11864123Skato	ZONE_FW_LEN,
11964123Skato	ZONE_FW_WP,
12064123Skato	ZONE_FW_TYPE,
12164123Skato	ZONE_FW_COND,
12264123Skato	ZONE_FW_SEQ,
12364123Skato	ZONE_FW_RESET,
12464123Skato	ZONE_NUM_FIELDS
12564123Skato} zone_field_widths;
12664123Skato
12764123Skatozone_print_status zone_rz_print(uint8_t *data_ptr, uint32_t valid_len,
12864123Skato				int ata_format, zone_output_flags out_flags,
12964123Skato				int first_pass, uint64_t *next_start_lba);
13064123Skato
13164123Skato
13264123Skatozone_print_status
13364123Skatozone_rz_print(uint8_t *data_ptr, uint32_t valid_len, int ata_format,
13464123Skato	      zone_output_flags out_flags, int first_pass,
13564123Skato	      uint64_t *next_start_lba)
13664123Skato{
13764123Skato	struct scsi_report_zones_hdr *hdr = NULL;
13864123Skato	struct scsi_report_zones_desc *desc = NULL;
13964123Skato	uint32_t hdr_len, len;
14064123Skato	uint64_t max_lba, next_lba = 0;
14164123Skato	int more_data = 0;
14264123Skato	zone_print_status status = ZONE_PRINT_OK;
14364123Skato	char tmpstr[80];
14464123Skato	int field_widths[ZONE_NUM_FIELDS];
14564123Skato	char word_sep;
14664123Skato
14764123Skato	if (valid_len < sizeof(*hdr)) {
14864123Skato		status = ZONE_PRINT_ERROR;
14964123Skato		goto bailout;
15064123Skato	}
15164123Skato
15264123Skato	hdr = (struct scsi_report_zones_hdr *)data_ptr;
15364123Skato
15464123Skato	field_widths[ZONE_FW_START] = 11;
15564123Skato	field_widths[ZONE_FW_LEN] = 6;
15664123Skato	field_widths[ZONE_FW_WP] = 11;
15764123Skato	field_widths[ZONE_FW_TYPE] = 13;
15864123Skato	field_widths[ZONE_FW_COND] = 13;
15964123Skato	field_widths[ZONE_FW_SEQ] = 14;
16064123Skato	field_widths[ZONE_FW_RESET] = 16;
16164123Skato
16264123Skato	if (ata_format == 0) {
16364123Skato		hdr_len = scsi_4btoul(hdr->length);
16464123Skato		max_lba = scsi_8btou64(hdr->maximum_lba);
16564123Skato	} else {
16664123Skato		hdr_len = le32dec(hdr->length);
16764123Skato		max_lba = le64dec(hdr->maximum_lba);
16864123Skato	}
16964123Skato
17064123Skato	if (hdr_len > (valid_len + sizeof(*hdr))) {
17164123Skato		more_data = 1;
17264123Skato		status = ZONE_PRINT_MORE_DATA;
17364123Skato	}
17464123Skato
17564123Skato	len = MIN(valid_len - sizeof(*hdr), hdr_len);
17664123Skato
17764123Skato	if (out_flags == ZONE_OF_SCRIPT)
17864123Skato		word_sep = '_';
17964123Skato	else
18064123Skato		word_sep = ' ';
18164123Skato
18264123Skato	if ((out_flags != ZONE_OF_SCRIPT)
18364123Skato	 && (first_pass != 0)) {
18464123Skato		printf("%zu zones, Maximum LBA %#jx (%ju)\n",
18564123Skato		    hdr_len / sizeof(*desc), (uintmax_t)max_lba,
18664123Skato		    (uintmax_t)max_lba);
18764123Skato
18864123Skato		switch (hdr->byte4 & SRZ_SAME_MASK) {
18964123Skato		case SRZ_SAME_ALL_DIFFERENT:
19064123Skato			printf("Zone lengths and types may vary\n");
19164123Skato			break;
19264123Skato		case SRZ_SAME_ALL_SAME:
19364123Skato			printf("Zone lengths and types are all the same\n");
19464123Skato			break;
19564123Skato		case SRZ_SAME_LAST_DIFFERENT:
19664123Skato			printf("Zone types are the same, last zone length "
19764123Skato			    "differs\n");
19864123Skato			break;
19964123Skato		case SRZ_SAME_TYPES_DIFFERENT:
20064123Skato			printf("Zone lengths are the same, types vary\n");
20164123Skato			break;
20264123Skato		default:
20364123Skato			printf("Unknown SAME field value %#x\n",
20464123Skato			    hdr->byte4 & SRZ_SAME_MASK);
20564123Skato			break;
20664123Skato		}
20764123Skato	}
20864123Skato	if (out_flags == ZONE_OF_SUMMARY) {
20964123Skato		status = ZONE_PRINT_OK;
21064123Skato		goto bailout;
21164123Skato	}
21264123Skato
21364123Skato	if ((out_flags == ZONE_OF_NORMAL)
21464123Skato	 && (first_pass != 0)) {
21564123Skato		printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
21664123Skato		    field_widths[ZONE_FW_START], "Start LBA",
21764123Skato		    field_widths[ZONE_FW_LEN], "Length",
21864123Skato		    field_widths[ZONE_FW_WP], "WP LBA",
21964123Skato		    field_widths[ZONE_FW_TYPE], "Zone Type",
22064123Skato		    field_widths[ZONE_FW_COND], "Condition",
22164123Skato		    field_widths[ZONE_FW_SEQ], "Sequential",
22264123Skato		    field_widths[ZONE_FW_RESET], "Reset");
22364123Skato	}
22464123Skato
22564123Skato	for (desc = &hdr->desc_list[0]; len >= sizeof(*desc);
22664123Skato	     len -= sizeof(*desc), desc++) {
22764123Skato		uint64_t length, start_lba, wp_lba;
22864123Skato
22964123Skato		if (ata_format == 0) {
23064123Skato			length = scsi_8btou64(desc->zone_length);
23164123Skato			start_lba = scsi_8btou64(desc->zone_start_lba);
23264123Skato			wp_lba = scsi_8btou64(desc->write_pointer_lba);
23364123Skato		} else {
23464123Skato			length = le64dec(desc->zone_length);
23564123Skato			start_lba = le64dec(desc->zone_start_lba);
23664123Skato			wp_lba = le64dec(desc->write_pointer_lba);
23764123Skato		}
23864123Skato
23964123Skato		printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
24064123Skato		    (uintmax_t)start_lba, field_widths[ZONE_FW_LEN],
24164123Skato		    (uintmax_t)length, field_widths[ZONE_FW_WP],
24264123Skato		    (uintmax_t)wp_lba);
24364123Skato
24464123Skato		switch (desc->zone_type & SRZ_TYPE_MASK) {
24564123Skato		case SRZ_TYPE_CONVENTIONAL:
24664123Skato			snprintf(tmpstr, sizeof(tmpstr), "Conventional");
24764123Skato			break;
24864123Skato		case SRZ_TYPE_SEQ_PREFERRED:
24964123Skato		case SRZ_TYPE_SEQ_REQUIRED:
25064123Skato			snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
25164123Skato			    word_sep, ((desc->zone_type & SRZ_TYPE_MASK) ==
25264123Skato			    SRZ_TYPE_SEQ_PREFERRED) ? "Preferred" :
25364123Skato			    "Required");
25464123Skato			break;
25564123Skato		default:
25664123Skato			snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
25764123Skato			    word_sep, word_sep,desc->zone_type &
25864123Skato			    SRZ_TYPE_MASK);
25964123Skato			break;
26064123Skato		}
26164123Skato		printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
26264123Skato
26364123Skato		switch (desc->zone_flags & SRZ_ZONE_COND_MASK) {
26464123Skato		case SRZ_ZONE_COND_NWP:
26564123Skato			snprintf(tmpstr, sizeof(tmpstr), "NWP");
26664123Skato			break;
26764123Skato		case SRZ_ZONE_COND_EMPTY:
26864123Skato			snprintf(tmpstr, sizeof(tmpstr), "Empty");
26964123Skato			break;
27064123Skato		case SRZ_ZONE_COND_IMP_OPEN:
27164123Skato			snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
27264123Skato			    word_sep);
27364123Skato			break;
27464123Skato		case SRZ_ZONE_COND_EXP_OPEN:
27564123Skato			snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
27664123Skato			    word_sep);
27764123Skato			break;
27864123Skato		case SRZ_ZONE_COND_CLOSED:
27964123Skato			snprintf(tmpstr, sizeof(tmpstr), "Closed");
28064123Skato			break;
28164123Skato		case SRZ_ZONE_COND_READONLY:
28264123Skato			snprintf(tmpstr, sizeof(tmpstr), "Readonly");
28364123Skato			break;
28464123Skato		case SRZ_ZONE_COND_FULL:
28564123Skato			snprintf(tmpstr, sizeof(tmpstr), "Full");
28664123Skato			break;
28764123Skato		case SRZ_ZONE_COND_OFFLINE:
28864123Skato			snprintf(tmpstr, sizeof(tmpstr), "Offline");
28964123Skato			break;
29064123Skato		default:
29164123Skato			snprintf(tmpstr, sizeof(tmpstr), "%#x",
29264123Skato			    desc->zone_flags & SRZ_ZONE_COND_MASK);
29364123Skato			break;
29464123Skato		}
29564123Skato
29664123Skato		printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
29764123Skato
29864123Skato		if (desc->zone_flags & SRZ_ZONE_NON_SEQ)
29964123Skato			snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
30064123Skato			    word_sep);
30164123Skato		else
302168457Skato			snprintf(tmpstr, sizeof(tmpstr), "Sequential");
303168457Skato
304168457Skato		printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
305168457Skato
306168457Skato		if (desc->zone_flags & SRZ_ZONE_RESET)
30764123Skato			snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
30864123Skato			    word_sep);
30964123Skato		else
31064123Skato			snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
31164123Skato			    word_sep, word_sep);
31264123Skato
31364123Skato		printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
31464123Skato
31564123Skato		next_lba = start_lba + length;
31664123Skato	}
31764123Skatobailout:
31864123Skato	*next_start_lba = next_lba;
31964123Skato
32064123Skato	return (status);
32164123Skato}
32264123Skato
32364123Skatoint
32464123Skatozone(struct cam_device *device, int argc, char **argv, char *combinedopt,
32564123Skato     int task_attr, int retry_count, int timeout, int verbosemode __unused)
32664123Skato{
32764123Skato	union ccb *ccb = NULL;
32864123Skato	int action = -1, rep_option = -1;
32964123Skato	int all_zones = 0;
33064123Skato	uint64_t lba = 0;
33164123Skato	int error = 0;
33264123Skato	uint8_t *data_ptr = NULL;
33364123Skato	uint32_t alloc_len = 65536, valid_len = 0;
33464123Skato	camcontrol_devtype devtype;
33564123Skato	int ata_format = 0, use_ncq = 0;
33664123Skato	int first_pass = 1;
33764123Skato	zone_print_status zp_status;
33864123Skato	zone_output_flags out_flags = ZONE_OF_NORMAL;
33964123Skato	uint8_t *cdb_storage = NULL;
34064123Skato	int cdb_storage_len = 32;
34164123Skato	int c;
34264123Skato
34364123Skato	ccb = cam_getccb(device);
34464123Skato	if (ccb == NULL) {
34564123Skato		warnx("%s: error allocating CCB", __func__);
34664123Skato		error = 1;
34764123Skato		goto bailout;
34864123Skato	}
34964123Skato
35064123Skato	CCB_CLEAR_ALL_EXCEPT_HDR(ccb);
35164123Skato
35264123Skato	while ((c = getopt(argc, argv, combinedopt)) != -1) {
35364123Skato		switch (c) {
35464123Skato		case 'a':
35564123Skato			all_zones = 1;
35664123Skato			break;
35764123Skato		case 'c': {
35864123Skato			scsi_nv_status status;
35964123Skato			int entry_num;
36064123Skato
36164123Skato			status = scsi_get_nv(zone_cmd_map,
36264123Skato			    (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
36364123Skato			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
36464123Skato			if (status == SCSI_NV_FOUND)
36564123Skato				action = zone_cmd_map[entry_num].value;
36664123Skato			else {
36764123Skato				warnx("%s: %s: %s option %s", __func__,
36864123Skato				    (status == SCSI_NV_AMBIGUOUS) ?
36964123Skato				    "ambiguous" : "invalid", "zone command",
37064123Skato				    optarg);
37164123Skato				error = 1;
37264123Skato				goto bailout;
37364123Skato			}
37464123Skato			break;
37564123Skato		}
37664123Skato		case 'l': {
37764123Skato			char *endptr;
37864123Skato
37964123Skato			lba = strtoull(optarg, &endptr, 0);
38064123Skato			if (*endptr != '\0') {
38164123Skato				warnx("%s: invalid lba argument %s", __func__,
38264123Skato				    optarg);
38364123Skato				error = 1;
38464123Skato				goto bailout;
38564123Skato			}
38664123Skato			break;
38764123Skato		}
38864123Skato		case 'N':
38964123Skato			use_ncq = 1;
39064123Skato			break;
39164123Skato		case 'o': {
39264123Skato			scsi_nv_status status;
39364123Skato			int entry_num;
39464123Skato
39564123Skato			status = scsi_get_nv(zone_rep_opts,
39664123Skato			    (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])),
39764123Skato			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
39864123Skato			if (status == SCSI_NV_FOUND)
39964123Skato				rep_option = zone_rep_opts[entry_num].value;
40064123Skato			else {
40164123Skato				warnx("%s: %s: %s option %s", __func__,
40264123Skato				    (status == SCSI_NV_AMBIGUOUS) ?
40364123Skato				    "ambiguous" : "invalid", "report zones",
40464123Skato				    optarg);
40564123Skato				error = 1;
40664123Skato				goto bailout;
40764123Skato			}
40864123Skato			break;
40964123Skato		}
41064123Skato		case 'P': {
41164123Skato			scsi_nv_status status;
41264123Skato			int entry_num;
41364123Skato
41464123Skato			status = scsi_get_nv(zone_print_opts,
41564123Skato			    (sizeof(zone_print_opts) /
41664123Skato			    sizeof(zone_print_opts[0])), optarg, &entry_num,
41764123Skato			    SCSI_NV_FLAG_IG_CASE);
41864123Skato			if (status == SCSI_NV_FOUND)
41964123Skato				out_flags = zone_print_opts[entry_num].value;
42064123Skato			else {
42164123Skato				warnx("%s: %s: %s option %s", __func__,
42264123Skato				    (status == SCSI_NV_AMBIGUOUS) ?
42364123Skato				    "ambiguous" : "invalid", "print",
42464123Skato				    optarg);
42564295Skato				error = 1;
42664123Skato				goto bailout;
42764123Skato			}
42864123Skato			break;
42964123Skato		}
43064123Skato		default:
43164123Skato			break;
43264123Skato		}
43364123Skato	}
43464123Skato	if (action == -1) {
43564123Skato		warnx("%s: must specify -c <zone_cmd>", __func__);
43664123Skato		error = 1;
43764123Skato		goto bailout;
43864123Skato	}
43964123Skato	error = get_device_type(device, retry_count, timeout,
44064123Skato	    /*printerrors*/ 1, &devtype);
44164123Skato	if (error != 0)
44264123Skato		errx(1, "Unable to determine device type");
44364123Skato
44464123Skato	if (action == ZBC_IN_SA_REPORT_ZONES) {
44564123Skato
44664123Skato		data_ptr = malloc(alloc_len);
44764123Skato		if (data_ptr == NULL)
44864123Skato			err(1, "unable to allocate %u bytes", alloc_len);
44964123Skato
45064123Skatorestart_report:
451		bzero(data_ptr, alloc_len);
452
453		switch (devtype) {
454		case CC_DT_SCSI:
455			scsi_zbc_in(&ccb->csio,
456			    /*retries*/ retry_count,
457			    /*cbfcnp*/ NULL,
458			    /*tag_action*/ task_attr,
459			    /*service_action*/ action,
460			    /*zone_start_lba*/ lba,
461			    /*zone_options*/ (rep_option != -1) ?
462					      rep_option : 0,
463			    /*data_ptr*/ data_ptr,
464			    /*dxfer_len*/ alloc_len,
465			    /*sense_len*/ SSD_FULL_SIZE,
466			    /*timeout*/ timeout ? timeout : 60000);
467			break;
468		case CC_DT_ATA:
469		case CC_DT_ATA_BEHIND_SCSI: {
470			uint8_t command = 0;
471			uint8_t protocol = 0;
472			uint16_t features = 0, sector_count = 0;
473			uint32_t auxiliary = 0;
474
475			/*
476			 * XXX KDM support the partial bit?
477			 */
478			if (use_ncq == 0) {
479				command = ATA_ZAC_MANAGEMENT_IN;
480				features = action;
481				if (rep_option != -1)
482					features |= (rep_option << 8);
483				sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len);
484				protocol = AP_PROTO_DMA;
485			} else {
486				if (cdb_storage == NULL)
487					cdb_storage = calloc(cdb_storage_len, 1);
488				if (cdb_storage == NULL)
489					err(1, "couldn't allocate memory");
490
491				command = ATA_RECV_FPDMA_QUEUED;
492				features = ZAC_ATA_SECTOR_COUNT(alloc_len);
493				sector_count = ATA_RFPDMA_ZAC_MGMT_IN << 8;
494				auxiliary = action & 0xf;
495				if (rep_option != -1)
496					auxiliary |= rep_option << 8;
497				protocol = AP_PROTO_FPDMA;
498			}
499
500			error = build_ata_cmd(ccb,
501			    /*retry_count*/ retry_count,
502			    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
503			    /*tag_action*/ task_attr,
504			    /*protocol*/ protocol,
505			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
506					  AP_FLAG_TLEN_SECT_CNT |
507					  AP_FLAG_TDIR_FROM_DEV,
508			    /*features*/ features,
509			    /*sector_count*/ sector_count,
510			    /*lba*/ lba,
511			    /*command*/ command,
512			    /*auxiliary*/ auxiliary,
513			    /*data_ptr*/ data_ptr,
514			    /*dxfer_len*/ ZAC_ATA_SECTOR_COUNT(alloc_len)*512,
515			    /*cdb_storage*/ cdb_storage,
516			    /*cdb_storage_len*/ cdb_storage_len,
517			    /*sense_len*/ SSD_FULL_SIZE,
518			    /*timeout*/ timeout ? timeout : 60000,
519			    /*is48bit*/ 1,
520			    /*devtype*/ devtype);
521
522			if (error != 0) {
523				warnx("%s: build_ata_cmd() failed, likely "
524				    "programmer error", __func__);
525				goto bailout;
526			}
527
528			ata_format = 1;
529
530			break;
531		}
532		default:
533			warnx("%s: Unknown device type %d", __func__,devtype);
534			error = 1;
535			goto bailout;
536			break; /*NOTREACHED*/
537		}
538	} else {
539		/*
540		 * XXX KDM the current methodology is to always send ATA
541		 * commands to ATA devices.  Need to figure out how to
542		 * detect whether a SCSI to ATA translation layer will
543		 * translate ZBC IN/OUT commands to the appropriate ZAC
544		 * command.
545		 */
546		switch (devtype) {
547		case CC_DT_SCSI:
548			scsi_zbc_out(&ccb->csio,
549			    /*retries*/ retry_count,
550			    /*cbfcnp*/ NULL,
551			    /*tag_action*/ task_attr,
552			    /*service_action*/ action,
553			    /*zone_id*/ lba,
554			    /*zone_flags*/ (all_zones != 0) ? ZBC_OUT_ALL : 0,
555			    /*data_ptr*/ NULL,
556			    /*dxfer_len*/ 0,
557			    /*sense_len*/ SSD_FULL_SIZE,
558			    /*timeout*/ timeout ? timeout : 60000);
559			break;
560		case CC_DT_ATA:
561		case CC_DT_ATA_BEHIND_SCSI: {
562			uint8_t command = 0;
563			uint8_t protocol = 0;
564			uint16_t features = 0, sector_count = 0;
565			uint32_t auxiliary = 0;
566
567			/*
568			 * Note that we're taking advantage of the fact
569			 * that the action numbers are the same between the
570			 * ZBC and ZAC specs.
571			 */
572
573			if (use_ncq == 0) {
574				protocol = AP_PROTO_NON_DATA;
575				command = ATA_ZAC_MANAGEMENT_OUT;
576				features = action & 0xf;
577				if (all_zones != 0)
578					features |= (ZBC_OUT_ALL << 8);
579			} else {
580				cdb_storage = calloc(cdb_storage_len, 1);
581				if (cdb_storage == NULL)
582					err(1, "couldn't allocate memory");
583
584				protocol = AP_PROTO_FPDMA;
585				command = ATA_NCQ_NON_DATA;
586				features = ATA_NCQ_ZAC_MGMT_OUT;
587				auxiliary = action & 0xf;
588				if (all_zones != 0)
589					auxiliary |= (ZBC_OUT_ALL << 8);
590			}
591
592
593			error = build_ata_cmd(ccb,
594			    /*retry_count*/ retry_count,
595			    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
596			    /*tag_action*/ task_attr,
597			    /*protocol*/ AP_PROTO_NON_DATA,
598			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
599					  AP_FLAG_TLEN_NO_DATA,
600			    /*features*/ features,
601			    /*sector_count*/ sector_count,
602			    /*lba*/ lba,
603			    /*command*/ command,
604			    /*auxiliary*/ auxiliary,
605			    /*data_ptr*/ NULL,
606			    /*dxfer_len*/ 0,
607			    /*cdb_storage*/ cdb_storage,
608			    /*cdb_storage_len*/ cdb_storage_len,
609			    /*sense_len*/ SSD_FULL_SIZE,
610			    /*timeout*/ timeout ? timeout : 60000,
611			    /*is48bit*/ 1,
612			    /*devtype*/ devtype);
613			if (error != 0) {
614				warnx("%s: build_ata_cmd() failed, likely "
615				    "programmer error", __func__);
616				goto bailout;
617			}
618			ata_format = 1;
619			break;
620		}
621		default:
622			warnx("%s: Unknown device type %d", __func__,devtype);
623			error = 1;
624			goto bailout;
625			break; /*NOTREACHED*/
626		}
627	}
628
629	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
630	if (retry_count > 0)
631		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
632
633	error = cam_send_ccb(device, ccb);
634	if (error != 0) {
635		warn("error sending %s %s CCB", (devtype == CC_DT_SCSI) ?
636		     "ZBC" : "ZAC Management",
637		     (action == ZBC_IN_SA_REPORT_ZONES) ? "In" : "Out");
638		error = -1;
639		goto bailout;
640	}
641
642	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
643		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
644		error = 1;
645		goto bailout;
646	}
647
648	/*
649	 * If we aren't reading the list of zones, we're done.
650	 */
651	if (action != ZBC_IN_SA_REPORT_ZONES)
652		goto bailout;
653
654	if (ccb->ccb_h.func_code == XPT_SCSI_IO)
655		valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
656	else
657		valid_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
658
659	zp_status = zone_rz_print(data_ptr, valid_len, ata_format, out_flags,
660	    first_pass, &lba);
661
662	if (zp_status == ZONE_PRINT_MORE_DATA) {
663		bzero(ccb, sizeof(*ccb));
664		first_pass = 0;
665		if (cdb_storage != NULL)
666			bzero(cdb_storage, cdb_storage_len);
667		goto restart_report;
668	} else if (zp_status == ZONE_PRINT_ERROR)
669		error = 1;
670bailout:
671	if (ccb != NULL)
672		cam_freeccb(ccb);
673
674	free(data_ptr);
675	free(cdb_storage);
676
677	return (error);
678}
679