zone.c revision 300207
1/*-
2 * Copyright (c) 2015, 2016 Spectra Logic Corporation
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions, and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    substantially similar to the "NO WARRANTY" disclaimer below
13 *    ("Disclaimer") and any redistribution must be conditioned upon
14 *    including a substantially similar Disclaimer requirement for further
15 *    binary redistribution.
16 *
17 * NO WARRANTY
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGES.
29 *
30 * Authors: Ken Merry           (Spectra Logic Corporation)
31 */
32/*
33 * SCSI and ATA Shingled Media Recording (SMR) support for camcontrol(8).
34 * This is an implementation of the SCSI ZBC and ATA ZAC specs.
35 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: head/sbin/camcontrol/zone.c 300207 2016-05-19 14:08:36Z ken $");
39
40#include <sys/ioctl.h>
41#include <sys/stdint.h>
42#include <sys/types.h>
43#include <sys/endian.h>
44#include <sys/sbuf.h>
45#include <sys/queue.h>
46#include <sys/chio.h>
47
48#include <stdio.h>
49#include <stdlib.h>
50#include <inttypes.h>
51#include <unistd.h>
52#include <string.h>
53#include <strings.h>
54#include <fcntl.h>
55#include <ctype.h>
56#include <limits.h>
57#include <err.h>
58#include <locale.h>
59
60#include <cam/cam.h>
61#include <cam/cam_debug.h>
62#include <cam/cam_ccb.h>
63#include <cam/scsi/scsi_all.h>
64#include <cam/scsi/scsi_da.h>
65#include <cam/scsi/scsi_pass.h>
66#include <cam/scsi/scsi_ch.h>
67#include <cam/scsi/scsi_message.h>
68#include <camlib.h>
69#include "camcontrol.h"
70
71static struct scsi_nv zone_cmd_map[] = {
72	{ "rz", ZBC_IN_SA_REPORT_ZONES },
73	{ "reportzones", ZBC_IN_SA_REPORT_ZONES },
74	{ "close", ZBC_OUT_SA_CLOSE },
75	{ "finish", ZBC_OUT_SA_FINISH },
76	{ "open", ZBC_OUT_SA_OPEN },
77	{ "rwp", ZBC_OUT_SA_RWP }
78};
79
80static struct scsi_nv zone_rep_opts[] = {
81	{ "all", ZBC_IN_REP_ALL_ZONES },
82	{ "empty", ZBC_IN_REP_EMPTY },
83	{ "imp_open", ZBC_IN_REP_IMP_OPEN },
84	{ "exp_open", ZBC_IN_REP_EXP_OPEN },
85	{ "closed", ZBC_IN_REP_CLOSED },
86	{ "full", ZBC_IN_REP_FULL },
87	{ "readonly", ZBC_IN_REP_READONLY },
88	{ "ro", ZBC_IN_REP_READONLY },
89	{ "offline", ZBC_IN_REP_OFFLINE },
90	{ "rwp", ZBC_IN_REP_RESET },
91	{ "reset", ZBC_IN_REP_RESET },
92	{ "nonseq", ZBC_IN_REP_NON_SEQ },
93	{ "nonwp", ZBC_IN_REP_NON_WP }
94};
95
96typedef enum {
97	ZONE_OF_NORMAL	= 0x00,
98	ZONE_OF_SUMMARY	= 0x01,
99	ZONE_OF_SCRIPT	= 0x02
100} zone_output_flags;
101
102static struct scsi_nv zone_print_opts[] = {
103	{ "normal", ZONE_OF_NORMAL },
104	{ "summary", ZONE_OF_SUMMARY },
105	{ "script", ZONE_OF_SCRIPT }
106};
107
108#define	ZAC_ATA_SECTOR_COUNT(bcount)	(((bcount) / 512) & 0xffff)
109
110typedef enum {
111	ZONE_PRINT_OK,
112	ZONE_PRINT_MORE_DATA,
113	ZONE_PRINT_ERROR
114} zone_print_status;
115
116typedef enum {
117	ZONE_FW_START,
118	ZONE_FW_LEN,
119	ZONE_FW_WP,
120	ZONE_FW_TYPE,
121	ZONE_FW_COND,
122	ZONE_FW_SEQ,
123	ZONE_FW_RESET,
124	ZONE_NUM_FIELDS
125} zone_field_widths;
126
127zone_print_status zone_rz_print(uint8_t *data_ptr, uint32_t valid_len,
128				int ata_format, zone_output_flags out_flags,
129				int first_pass, uint64_t *next_start_lba);
130
131
132zone_print_status
133zone_rz_print(uint8_t *data_ptr, uint32_t valid_len, int ata_format,
134	      zone_output_flags out_flags, int first_pass,
135	      uint64_t *next_start_lba)
136{
137	struct scsi_report_zones_hdr *hdr = NULL;
138	struct scsi_report_zones_desc *desc = NULL;
139	uint32_t hdr_len, len;
140	uint64_t max_lba, next_lba = 0;
141	int more_data = 0;
142	zone_print_status status = ZONE_PRINT_OK;
143	char tmpstr[80];
144	int field_widths[ZONE_NUM_FIELDS];
145	char word_sep;
146
147	if (valid_len < sizeof(*hdr)) {
148		status = ZONE_PRINT_ERROR;
149		goto bailout;
150	}
151
152	hdr = (struct scsi_report_zones_hdr *)data_ptr;
153
154	field_widths[ZONE_FW_START] = 11;
155	field_widths[ZONE_FW_LEN] = 6;
156	field_widths[ZONE_FW_WP] = 11;
157	field_widths[ZONE_FW_TYPE] = 13;
158	field_widths[ZONE_FW_COND] = 13;
159	field_widths[ZONE_FW_SEQ] = 14;
160	field_widths[ZONE_FW_RESET] = 16;
161
162	if (ata_format == 0) {
163		hdr_len = scsi_4btoul(hdr->length);
164		max_lba = scsi_8btou64(hdr->maximum_lba);
165	} else {
166		hdr_len = le32dec(hdr->length);
167		max_lba = le64dec(hdr->maximum_lba);
168	}
169
170	if (hdr_len > (valid_len + sizeof(*hdr))) {
171		more_data = 1;
172		status = ZONE_PRINT_MORE_DATA;
173	}
174
175	len = MIN(valid_len - sizeof(*hdr), hdr_len);
176
177	if (out_flags == ZONE_OF_SCRIPT)
178		word_sep = '_';
179	else
180		word_sep = ' ';
181
182	if ((out_flags != ZONE_OF_SCRIPT)
183	 && (first_pass != 0)) {
184		printf("%zu zones, Maximum LBA %#jx (%ju)\n",
185		    hdr_len / sizeof(*desc), (uintmax_t)max_lba,
186		    (uintmax_t)max_lba);
187
188		switch (hdr->byte4 & SRZ_SAME_MASK) {
189		case SRZ_SAME_ALL_DIFFERENT:
190			printf("Zone lengths and types may vary\n");
191			break;
192		case SRZ_SAME_ALL_SAME:
193			printf("Zone lengths and types are all the same\n");
194			break;
195		case SRZ_SAME_LAST_DIFFERENT:
196			printf("Zone types are the same, last zone length "
197			    "differs\n");
198			break;
199		case SRZ_SAME_TYPES_DIFFERENT:
200			printf("Zone lengths are the same, types vary\n");
201			break;
202		default:
203			printf("Unknown SAME field value %#x\n",
204			    hdr->byte4 & SRZ_SAME_MASK);
205			break;
206		}
207	}
208	if (out_flags == ZONE_OF_SUMMARY) {
209		status = ZONE_PRINT_OK;
210		goto bailout;
211	}
212
213	if ((out_flags == ZONE_OF_NORMAL)
214	 && (first_pass != 0)) {
215		printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
216		    field_widths[ZONE_FW_START], "Start LBA",
217		    field_widths[ZONE_FW_LEN], "Length",
218		    field_widths[ZONE_FW_WP], "WP LBA",
219		    field_widths[ZONE_FW_TYPE], "Zone Type",
220		    field_widths[ZONE_FW_COND], "Condition",
221		    field_widths[ZONE_FW_SEQ], "Sequential",
222		    field_widths[ZONE_FW_RESET], "Reset");
223	}
224
225	for (desc = &hdr->desc_list[0]; len >= sizeof(*desc);
226	     len -= sizeof(*desc), desc++) {
227		uint64_t length, start_lba, wp_lba;
228
229		if (ata_format == 0) {
230			length = scsi_8btou64(desc->zone_length);
231			start_lba = scsi_8btou64(desc->zone_start_lba);
232			wp_lba = scsi_8btou64(desc->write_pointer_lba);
233		} else {
234			length = le64dec(desc->zone_length);
235			start_lba = le64dec(desc->zone_start_lba);
236			wp_lba = le64dec(desc->write_pointer_lba);
237		}
238
239		printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
240		    (uintmax_t)start_lba, field_widths[ZONE_FW_LEN],
241		    (uintmax_t)length, field_widths[ZONE_FW_WP],
242		    (uintmax_t)wp_lba);
243
244		switch (desc->zone_type & SRZ_TYPE_MASK) {
245		case SRZ_TYPE_CONVENTIONAL:
246			snprintf(tmpstr, sizeof(tmpstr), "Conventional");
247			break;
248		case SRZ_TYPE_SEQ_PREFERRED:
249		case SRZ_TYPE_SEQ_REQUIRED:
250			snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
251			    word_sep, ((desc->zone_type & SRZ_TYPE_MASK) ==
252			    SRZ_TYPE_SEQ_PREFERRED) ? "Preferred" :
253			    "Required");
254			break;
255		default:
256			snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
257			    word_sep, word_sep,desc->zone_type &
258			    SRZ_TYPE_MASK);
259			break;
260		}
261		printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
262
263		switch (desc->zone_flags & SRZ_ZONE_COND_MASK) {
264		case SRZ_ZONE_COND_NWP:
265			snprintf(tmpstr, sizeof(tmpstr), "NWP");
266			break;
267		case SRZ_ZONE_COND_EMPTY:
268			snprintf(tmpstr, sizeof(tmpstr), "Empty");
269			break;
270		case SRZ_ZONE_COND_IMP_OPEN:
271			snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen",
272			    word_sep);
273			break;
274		case SRZ_ZONE_COND_EXP_OPEN:
275			snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
276			    word_sep);
277			break;
278		case SRZ_ZONE_COND_CLOSED:
279			snprintf(tmpstr, sizeof(tmpstr), "Closed");
280			break;
281		case SRZ_ZONE_COND_READONLY:
282			snprintf(tmpstr, sizeof(tmpstr), "Readonly");
283			break;
284		case SRZ_ZONE_COND_FULL:
285			snprintf(tmpstr, sizeof(tmpstr), "Full");
286			break;
287		case SRZ_ZONE_COND_OFFLINE:
288			snprintf(tmpstr, sizeof(tmpstr), "Offline");
289			break;
290		default:
291			snprintf(tmpstr, sizeof(tmpstr), "%#x",
292			    desc->zone_flags & SRZ_ZONE_COND_MASK);
293			break;
294		}
295
296		printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
297
298		if (desc->zone_flags & SRZ_ZONE_NON_SEQ)
299			snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
300			    word_sep);
301		else
302			snprintf(tmpstr, sizeof(tmpstr), "Sequential");
303
304		printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
305
306		if (desc->zone_flags & SRZ_ZONE_RESET)
307			snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
308			    word_sep);
309		else
310			snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
311			    word_sep, word_sep);
312
313		printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
314
315		next_lba = start_lba + length;
316	}
317bailout:
318	*next_start_lba = next_lba;
319
320	return (status);
321}
322
323int
324zone(struct cam_device *device, int argc, char **argv, char *combinedopt,
325     int retry_count, int timeout, int verbosemode __unused)
326{
327	union ccb *ccb = NULL;
328	int action = -1, rep_option = -1;
329	int all_zones = 0;
330	uint64_t lba = 0;
331	int error = 0;
332	uint8_t *data_ptr = NULL;
333	uint32_t alloc_len = 65536, valid_len = 0;
334	camcontrol_devtype devtype;
335	int ata_format = 0, use_ncq = 0;
336	int first_pass = 1;
337	zone_print_status zp_status;
338	zone_output_flags out_flags = ZONE_OF_NORMAL;
339	uint8_t *cdb_storage = NULL;
340	int cdb_storage_len = 32;
341	int c;
342
343	ccb = cam_getccb(device);
344	if (ccb == NULL) {
345		warnx("%s: error allocating CCB", __func__);
346		error = 1;
347		goto bailout;
348	}
349
350	bzero(&(&ccb->ccb_h)[1],
351	      sizeof(union ccb) - sizeof(struct ccb_hdr));
352
353	while ((c = getopt(argc, argv, combinedopt)) != -1) {
354		switch (c) {
355		case 'a':
356			all_zones = 1;
357			break;
358		case 'c': {
359			scsi_nv_status status;
360			int entry_num;
361
362			status = scsi_get_nv(zone_cmd_map,
363			    (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
364			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
365			if (status == SCSI_NV_FOUND)
366				action = zone_cmd_map[entry_num].value;
367			else {
368				warnx("%s: %s: %s option %s", __func__,
369				    (status == SCSI_NV_AMBIGUOUS) ?
370				    "ambiguous" : "invalid", "zone command",
371				    optarg);
372				error = 1;
373				goto bailout;
374			}
375			break;
376		}
377		case 'l': {
378			char *endptr;
379
380			lba = strtoull(optarg, &endptr, 0);
381			if (*endptr != '\0') {
382				warnx("%s: invalid lba argument %s", __func__,
383				    optarg);
384				error = 1;
385				goto bailout;
386			}
387			break;
388		}
389		case 'N':
390			use_ncq = 1;
391			break;
392		case 'o': {
393			scsi_nv_status status;
394			int entry_num;
395
396			status = scsi_get_nv(zone_rep_opts,
397			    (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])),
398			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
399			if (status == SCSI_NV_FOUND)
400				rep_option = zone_rep_opts[entry_num].value;
401			else {
402				warnx("%s: %s: %s option %s", __func__,
403				    (status == SCSI_NV_AMBIGUOUS) ?
404				    "ambiguous" : "invalid", "report zones",
405				    optarg);
406				error = 1;
407				goto bailout;
408			}
409			break;
410		}
411		case 'P': {
412			scsi_nv_status status;
413			int entry_num;
414
415			status = scsi_get_nv(zone_print_opts,
416			    (sizeof(zone_print_opts) /
417			    sizeof(zone_print_opts[0])), optarg, &entry_num,
418			    SCSI_NV_FLAG_IG_CASE);
419			if (status == SCSI_NV_FOUND)
420				out_flags = zone_print_opts[entry_num].value;
421			else {
422				warnx("%s: %s: %s option %s", __func__,
423				    (status == SCSI_NV_AMBIGUOUS) ?
424				    "ambiguous" : "invalid", "print",
425				    optarg);
426				error = 1;
427				goto bailout;
428			}
429			break;
430		}
431		default:
432			break;
433		}
434	}
435	if (action == -1) {
436		warnx("%s: must specify -c <zone_cmd>", __func__);
437		error = 1;
438		goto bailout;
439	}
440	error = get_device_type(device, retry_count, timeout,
441	    /*printerrors*/ 1, &devtype);
442	if (error != 0)
443		errx(1, "Unable to determine device type");
444
445	if (action == ZBC_IN_SA_REPORT_ZONES) {
446
447		data_ptr = malloc(alloc_len);
448		if (data_ptr == NULL)
449			err(1, "unable to allocate %u bytes", alloc_len);
450
451restart_report:
452		bzero(data_ptr, alloc_len);
453
454		switch (devtype) {
455		case CC_DT_SCSI:
456			scsi_zbc_in(&ccb->csio,
457			    /*retries*/ retry_count,
458			    /*cbfcnp*/ NULL,
459			    /*tag_action*/ MSG_SIMPLE_Q_TAG,
460			    /*service_action*/ action,
461			    /*zone_start_lba*/ lba,
462			    /*zone_options*/ (rep_option != -1) ?
463					      rep_option : 0,
464			    /*data_ptr*/ data_ptr,
465			    /*dxfer_len*/ alloc_len,
466			    /*sense_len*/ SSD_FULL_SIZE,
467			    /*timeout*/ timeout ? timeout : 60000);
468			break;
469		case CC_DT_ATA:
470		case CC_DT_ATA_BEHIND_SCSI: {
471			uint8_t command = 0;
472			uint8_t protocol = 0;
473			uint16_t features = 0, sector_count = 0;
474			uint32_t auxiliary = 0;
475
476			/*
477			 * XXX KDM support the partial bit?
478			 */
479			if (use_ncq == 0) {
480				command = ATA_ZAC_MANAGEMENT_IN;
481				features = action;
482				if (rep_option != -1)
483					features |= (rep_option << 8);
484				sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len);
485				protocol = AP_PROTO_DMA;
486			} else {
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*/ MSG_SIMPLE_Q_TAG,
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*/ MSG_SIMPLE_Q_TAG,
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*/ MSG_SIMPLE_Q_TAG,
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		goto restart_report;
666	} else if (zp_status == ZONE_PRINT_ERROR)
667		error = 1;
668bailout:
669	if (ccb != NULL)
670		cam_freeccb(ccb);
671
672	free(data_ptr);
673	free(cdb_storage);
674
675	return (error);
676}
677