xref: /freebsd/sbin/camcontrol/zone.c (revision 9768746b)
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$");
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 
71 static 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 
80 static 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 
96 typedef enum {
97 	ZONE_OF_NORMAL	= 0x00,
98 	ZONE_OF_SUMMARY	= 0x01,
99 	ZONE_OF_SCRIPT	= 0x02
100 } zone_output_flags;
101 
102 static 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 
110 typedef enum {
111 	ZONE_PRINT_OK,
112 	ZONE_PRINT_MORE_DATA,
113 	ZONE_PRINT_ERROR
114 } zone_print_status;
115 
116 typedef 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 
127 zone_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 
132 zone_print_status
133 zone_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 	}
317 bailout:
318 	*next_start_lba = next_lba;
319 
320 	return (status);
321 }
322 
323 int
324 zone(struct cam_device *device, int argc, char **argv, char *combinedopt,
325      int task_attr, 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 	while ((c = getopt(argc, argv, combinedopt)) != -1) {
351 		switch (c) {
352 		case 'a':
353 			all_zones = 1;
354 			break;
355 		case 'c': {
356 			scsi_nv_status status;
357 			int entry_num;
358 
359 			status = scsi_get_nv(zone_cmd_map,
360 			    (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
361 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
362 			if (status == SCSI_NV_FOUND)
363 				action = zone_cmd_map[entry_num].value;
364 			else {
365 				warnx("%s: %s: %s option %s", __func__,
366 				    (status == SCSI_NV_AMBIGUOUS) ?
367 				    "ambiguous" : "invalid", "zone command",
368 				    optarg);
369 				error = 1;
370 				goto bailout;
371 			}
372 			break;
373 		}
374 		case 'l': {
375 			char *endptr;
376 
377 			lba = strtoull(optarg, &endptr, 0);
378 			if (*endptr != '\0') {
379 				warnx("%s: invalid lba argument %s", __func__,
380 				    optarg);
381 				error = 1;
382 				goto bailout;
383 			}
384 			break;
385 		}
386 		case 'N':
387 			use_ncq = 1;
388 			break;
389 		case 'o': {
390 			scsi_nv_status status;
391 			int entry_num;
392 
393 			status = scsi_get_nv(zone_rep_opts,
394 			    (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])),
395 			    optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
396 			if (status == SCSI_NV_FOUND)
397 				rep_option = zone_rep_opts[entry_num].value;
398 			else {
399 				warnx("%s: %s: %s option %s", __func__,
400 				    (status == SCSI_NV_AMBIGUOUS) ?
401 				    "ambiguous" : "invalid", "report zones",
402 				    optarg);
403 				error = 1;
404 				goto bailout;
405 			}
406 			break;
407 		}
408 		case 'P': {
409 			scsi_nv_status status;
410 			int entry_num;
411 
412 			status = scsi_get_nv(zone_print_opts,
413 			    (sizeof(zone_print_opts) /
414 			    sizeof(zone_print_opts[0])), optarg, &entry_num,
415 			    SCSI_NV_FLAG_IG_CASE);
416 			if (status == SCSI_NV_FOUND)
417 				out_flags = zone_print_opts[entry_num].value;
418 			else {
419 				warnx("%s: %s: %s option %s", __func__,
420 				    (status == SCSI_NV_AMBIGUOUS) ?
421 				    "ambiguous" : "invalid", "print",
422 				    optarg);
423 				error = 1;
424 				goto bailout;
425 			}
426 			break;
427 		}
428 		default:
429 			break;
430 		}
431 	}
432 	if (action == -1) {
433 		warnx("%s: must specify -c <zone_cmd>", __func__);
434 		error = 1;
435 		goto bailout;
436 	}
437 	error = get_device_type(device, retry_count, timeout,
438 	    /*printerrors*/ 1, &devtype);
439 	if (error != 0)
440 		errx(1, "Unable to determine device type");
441 
442 	if (action == ZBC_IN_SA_REPORT_ZONES) {
443 
444 		data_ptr = malloc(alloc_len);
445 		if (data_ptr == NULL)
446 			err(1, "unable to allocate %u bytes", alloc_len);
447 
448 restart_report:
449 		bzero(data_ptr, alloc_len);
450 
451 		switch (devtype) {
452 		case CC_DT_SCSI:
453 			scsi_zbc_in(&ccb->csio,
454 			    /*retries*/ retry_count,
455 			    /*cbfcnp*/ NULL,
456 			    /*tag_action*/ task_attr,
457 			    /*service_action*/ action,
458 			    /*zone_start_lba*/ lba,
459 			    /*zone_options*/ (rep_option != -1) ?
460 					      rep_option : 0,
461 			    /*data_ptr*/ data_ptr,
462 			    /*dxfer_len*/ alloc_len,
463 			    /*sense_len*/ SSD_FULL_SIZE,
464 			    /*timeout*/ timeout ? timeout : 60000);
465 			break;
466 		case CC_DT_ATA:
467 		case CC_DT_SATL: {
468 			uint8_t command = 0;
469 			uint8_t protocol = 0;
470 			uint16_t features = 0, sector_count = 0;
471 			uint32_t auxiliary = 0;
472 
473 			/*
474 			 * XXX KDM support the partial bit?
475 			 */
476 			if (use_ncq == 0) {
477 				command = ATA_ZAC_MANAGEMENT_IN;
478 				features = action;
479 				if (rep_option != -1)
480 					features |= (rep_option << 8);
481 				sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len);
482 				protocol = AP_PROTO_DMA;
483 			} else {
484 				if (cdb_storage == NULL)
485 					cdb_storage = calloc(cdb_storage_len, 1);
486 				if (cdb_storage == NULL)
487 					err(1, "couldn't allocate memory");
488 
489 				command = ATA_RECV_FPDMA_QUEUED;
490 				features = ZAC_ATA_SECTOR_COUNT(alloc_len);
491 				sector_count = ATA_RFPDMA_ZAC_MGMT_IN << 8;
492 				auxiliary = action & 0xf;
493 				if (rep_option != -1)
494 					auxiliary |= rep_option << 8;
495 				protocol = AP_PROTO_FPDMA;
496 			}
497 
498 			error = build_ata_cmd(ccb,
499 			    /*retry_count*/ retry_count,
500 			    /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
501 			    /*tag_action*/ task_attr,
502 			    /*protocol*/ protocol,
503 			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
504 					  AP_FLAG_TLEN_SECT_CNT |
505 					  AP_FLAG_TDIR_FROM_DEV,
506 			    /*features*/ features,
507 			    /*sector_count*/ sector_count,
508 			    /*lba*/ lba,
509 			    /*command*/ command,
510 			    /*auxiliary*/ auxiliary,
511 			    /*data_ptr*/ data_ptr,
512 			    /*dxfer_len*/ ZAC_ATA_SECTOR_COUNT(alloc_len)*512,
513 			    /*cdb_storage*/ cdb_storage,
514 			    /*cdb_storage_len*/ cdb_storage_len,
515 			    /*sense_len*/ SSD_FULL_SIZE,
516 			    /*timeout*/ timeout ? timeout : 60000,
517 			    /*is48bit*/ 1,
518 			    /*devtype*/ devtype);
519 
520 			if (error != 0) {
521 				warnx("%s: build_ata_cmd() failed, likely "
522 				    "programmer error", __func__);
523 				goto bailout;
524 			}
525 
526 			ata_format = 1;
527 
528 			break;
529 		}
530 		default:
531 			warnx("%s: Unknown device type %d", __func__,devtype);
532 			error = 1;
533 			goto bailout;
534 			break; /*NOTREACHED*/
535 		}
536 	} else {
537 		/*
538 		 * XXX KDM the current methodology is to always send ATA
539 		 * commands to ATA devices.  Need to figure out how to
540 		 * detect whether a SCSI to ATA translation layer will
541 		 * translate ZBC IN/OUT commands to the appropriate ZAC
542 		 * command.
543 		 */
544 		switch (devtype) {
545 		case CC_DT_SCSI:
546 			scsi_zbc_out(&ccb->csio,
547 			    /*retries*/ retry_count,
548 			    /*cbfcnp*/ NULL,
549 			    /*tag_action*/ task_attr,
550 			    /*service_action*/ action,
551 			    /*zone_id*/ lba,
552 			    /*zone_flags*/ (all_zones != 0) ? ZBC_OUT_ALL : 0,
553 			    /*data_ptr*/ NULL,
554 			    /*dxfer_len*/ 0,
555 			    /*sense_len*/ SSD_FULL_SIZE,
556 			    /*timeout*/ timeout ? timeout : 60000);
557 			break;
558 		case CC_DT_ATA:
559 		case CC_DT_SATL: {
560 			uint8_t command = 0;
561 			uint8_t protocol = 0;
562 			uint16_t features = 0, sector_count = 0;
563 			uint32_t auxiliary = 0;
564 
565 			/*
566 			 * Note that we're taking advantage of the fact
567 			 * that the action numbers are the same between the
568 			 * ZBC and ZAC specs.
569 			 */
570 
571 			if (use_ncq == 0) {
572 				protocol = AP_PROTO_NON_DATA;
573 				command = ATA_ZAC_MANAGEMENT_OUT;
574 				features = action & 0xf;
575 				if (all_zones != 0)
576 					features |= (ZBC_OUT_ALL << 8);
577 			} else {
578 				cdb_storage = calloc(cdb_storage_len, 1);
579 				if (cdb_storage == NULL)
580 					err(1, "couldn't allocate memory");
581 
582 				protocol = AP_PROTO_FPDMA;
583 				command = ATA_NCQ_NON_DATA;
584 				features = ATA_NCQ_ZAC_MGMT_OUT;
585 				auxiliary = action & 0xf;
586 				if (all_zones != 0)
587 					auxiliary |= (ZBC_OUT_ALL << 8);
588 			}
589 
590 
591 			error = build_ata_cmd(ccb,
592 			    /*retry_count*/ retry_count,
593 			    /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
594 			    /*tag_action*/ task_attr,
595 			    /*protocol*/ AP_PROTO_NON_DATA,
596 			    /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
597 					  AP_FLAG_TLEN_NO_DATA,
598 			    /*features*/ features,
599 			    /*sector_count*/ sector_count,
600 			    /*lba*/ lba,
601 			    /*command*/ command,
602 			    /*auxiliary*/ auxiliary,
603 			    /*data_ptr*/ NULL,
604 			    /*dxfer_len*/ 0,
605 			    /*cdb_storage*/ cdb_storage,
606 			    /*cdb_storage_len*/ cdb_storage_len,
607 			    /*sense_len*/ SSD_FULL_SIZE,
608 			    /*timeout*/ timeout ? timeout : 60000,
609 			    /*is48bit*/ 1,
610 			    /*devtype*/ devtype);
611 			if (error != 0) {
612 				warnx("%s: build_ata_cmd() failed, likely "
613 				    "programmer error", __func__);
614 				goto bailout;
615 			}
616 			ata_format = 1;
617 			break;
618 		}
619 		default:
620 			warnx("%s: Unknown device type %d", __func__,devtype);
621 			error = 1;
622 			goto bailout;
623 			break; /*NOTREACHED*/
624 		}
625 	}
626 
627 	ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
628 	if (retry_count > 0)
629 		ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
630 
631 	error = cam_send_ccb(device, ccb);
632 	if (error != 0) {
633 		warn("error sending %s %s CCB", (devtype == CC_DT_SCSI) ?
634 		     "ZBC" : "ZAC Management",
635 		     (action == ZBC_IN_SA_REPORT_ZONES) ? "In" : "Out");
636 		error = -1;
637 		goto bailout;
638 	}
639 
640 	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
641 		cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
642 		error = 1;
643 		goto bailout;
644 	}
645 
646 	/*
647 	 * If we aren't reading the list of zones, we're done.
648 	 */
649 	if (action != ZBC_IN_SA_REPORT_ZONES)
650 		goto bailout;
651 
652 	if (ccb->ccb_h.func_code == XPT_SCSI_IO)
653 		valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
654 	else
655 		valid_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
656 
657 	zp_status = zone_rz_print(data_ptr, valid_len, ata_format, out_flags,
658 	    first_pass, &lba);
659 
660 	if (zp_status == ZONE_PRINT_MORE_DATA) {
661 		bzero(ccb, sizeof(*ccb));
662 		first_pass = 0;
663 		if (cdb_storage != NULL)
664 			bzero(cdb_storage, cdb_storage_len);
665 		goto restart_report;
666 	} else if (zp_status == ZONE_PRINT_ERROR)
667 		error = 1;
668 bailout:
669 	if (ccb != NULL)
670 		cam_freeccb(ccb);
671 
672 	free(data_ptr);
673 	free(cdb_storage);
674 
675 	return (error);
676 }
677