]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/camcontrol/zone.c
ssh: Update to OpenSSH 9.4p1
[FreeBSD/FreeBSD.git] / sbin / camcontrol / zone.c
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         zone_print_status status = ZONE_PRINT_OK;
142         char tmpstr[80];
143         int field_widths[ZONE_NUM_FIELDS];
144         char word_sep;
145
146         if (valid_len < sizeof(*hdr)) {
147                 status = ZONE_PRINT_ERROR;
148                 goto bailout;
149         }
150
151         hdr = (struct scsi_report_zones_hdr *)data_ptr;
152
153         field_widths[ZONE_FW_START] = 11;
154         field_widths[ZONE_FW_LEN] = 6;
155         field_widths[ZONE_FW_WP] = 11;
156         field_widths[ZONE_FW_TYPE] = 13;
157         field_widths[ZONE_FW_COND] = 13;
158         field_widths[ZONE_FW_SEQ] = 14;
159         field_widths[ZONE_FW_RESET] = 16;
160
161         if (ata_format == 0) {
162                 hdr_len = scsi_4btoul(hdr->length);
163                 max_lba = scsi_8btou64(hdr->maximum_lba);
164         } else {
165                 hdr_len = le32dec(hdr->length);
166                 max_lba = le64dec(hdr->maximum_lba);
167         }
168
169         if (hdr_len > (valid_len + sizeof(*hdr))) {
170                 status = ZONE_PRINT_MORE_DATA;
171         }
172
173         len = MIN(valid_len - sizeof(*hdr), hdr_len);
174
175         if (out_flags == ZONE_OF_SCRIPT)
176                 word_sep = '_';
177         else
178                 word_sep = ' ';
179
180         if ((out_flags != ZONE_OF_SCRIPT)
181          && (first_pass != 0)) {
182                 printf("%zu zones, Maximum LBA %#jx (%ju)\n",
183                     hdr_len / sizeof(*desc), (uintmax_t)max_lba,
184                     (uintmax_t)max_lba);
185
186                 switch (hdr->byte4 & SRZ_SAME_MASK) {
187                 case SRZ_SAME_ALL_DIFFERENT:
188                         printf("Zone lengths and types may vary\n");
189                         break;
190                 case SRZ_SAME_ALL_SAME:
191                         printf("Zone lengths and types are all the same\n");
192                         break;
193                 case SRZ_SAME_LAST_DIFFERENT:
194                         printf("Zone types are the same, last zone length "
195                             "differs\n");
196                         break;
197                 case SRZ_SAME_TYPES_DIFFERENT:
198                         printf("Zone lengths are the same, types vary\n");
199                         break;
200                 default:
201                         printf("Unknown SAME field value %#x\n",
202                             hdr->byte4 & SRZ_SAME_MASK);
203                         break;
204                 }
205         }
206         if (out_flags == ZONE_OF_SUMMARY) {
207                 status = ZONE_PRINT_OK;
208                 goto bailout;
209         }
210
211         if ((out_flags == ZONE_OF_NORMAL)
212          && (first_pass != 0)) {
213                 printf("%*s  %*s  %*s  %*s  %*s  %*s  %*s\n",
214                     field_widths[ZONE_FW_START], "Start LBA",
215                     field_widths[ZONE_FW_LEN], "Length",
216                     field_widths[ZONE_FW_WP], "WP LBA",
217                     field_widths[ZONE_FW_TYPE], "Zone Type",
218                     field_widths[ZONE_FW_COND], "Condition",
219                     field_widths[ZONE_FW_SEQ], "Sequential",
220                     field_widths[ZONE_FW_RESET], "Reset");
221         }
222
223         for (desc = &hdr->desc_list[0]; len >= sizeof(*desc);
224              len -= sizeof(*desc), desc++) {
225                 uint64_t length, start_lba, wp_lba;
226
227                 if (ata_format == 0) {
228                         length = scsi_8btou64(desc->zone_length);
229                         start_lba = scsi_8btou64(desc->zone_start_lba);
230                         wp_lba = scsi_8btou64(desc->write_pointer_lba);
231                 } else {
232                         length = le64dec(desc->zone_length);
233                         start_lba = le64dec(desc->zone_start_lba);
234                         wp_lba = le64dec(desc->write_pointer_lba);
235                 }
236
237                 printf("%#*jx, %*ju, %#*jx, ", field_widths[ZONE_FW_START],
238                     (uintmax_t)start_lba, field_widths[ZONE_FW_LEN],
239                     (uintmax_t)length, field_widths[ZONE_FW_WP],
240                     (uintmax_t)wp_lba);
241
242                 switch (desc->zone_type & SRZ_TYPE_MASK) {
243                 case SRZ_TYPE_CONVENTIONAL:
244                         snprintf(tmpstr, sizeof(tmpstr), "Conventional");
245                         break;
246                 case SRZ_TYPE_SEQ_PREFERRED:
247                 case SRZ_TYPE_SEQ_REQUIRED:
248                         snprintf(tmpstr, sizeof(tmpstr), "Seq%c%s",
249                             word_sep, ((desc->zone_type & SRZ_TYPE_MASK) ==
250                             SRZ_TYPE_SEQ_PREFERRED) ? "Preferred" :
251                             "Required");
252                         break;
253                 default:
254                         snprintf(tmpstr, sizeof(tmpstr), "Zone%ctype%c%#x",
255                             word_sep, word_sep,desc->zone_type &
256                             SRZ_TYPE_MASK);
257                         break;
258                 }
259                 printf("%*s, ", field_widths[ZONE_FW_TYPE], tmpstr);
260
261                 switch (desc->zone_flags & SRZ_ZONE_COND_MASK) {
262                 case SRZ_ZONE_COND_NWP:
263                         snprintf(tmpstr, sizeof(tmpstr), "NWP");
264                         break;
265                 case SRZ_ZONE_COND_EMPTY:
266                         snprintf(tmpstr, sizeof(tmpstr), "Empty");
267                         break;
268                 case SRZ_ZONE_COND_IMP_OPEN:
269                         snprintf(tmpstr, sizeof(tmpstr), "Implicit%cOpen", 
270                             word_sep);
271                         break;
272                 case SRZ_ZONE_COND_EXP_OPEN:
273                         snprintf(tmpstr, sizeof(tmpstr), "Explicit%cOpen",
274                             word_sep);
275                         break;
276                 case SRZ_ZONE_COND_CLOSED:
277                         snprintf(tmpstr, sizeof(tmpstr), "Closed");
278                         break;
279                 case SRZ_ZONE_COND_READONLY:
280                         snprintf(tmpstr, sizeof(tmpstr), "Readonly");
281                         break;
282                 case SRZ_ZONE_COND_FULL:
283                         snprintf(tmpstr, sizeof(tmpstr), "Full");
284                         break;
285                 case SRZ_ZONE_COND_OFFLINE:
286                         snprintf(tmpstr, sizeof(tmpstr), "Offline");
287                         break;
288                 default:
289                         snprintf(tmpstr, sizeof(tmpstr), "%#x",
290                             desc->zone_flags & SRZ_ZONE_COND_MASK);
291                         break;
292                 }
293
294                 printf("%*s, ", field_widths[ZONE_FW_COND], tmpstr);
295
296                 if (desc->zone_flags & SRZ_ZONE_NON_SEQ)
297                         snprintf(tmpstr, sizeof(tmpstr), "Non%cSequential",
298                             word_sep);
299                 else
300                         snprintf(tmpstr, sizeof(tmpstr), "Sequential");
301
302                 printf("%*s, ", field_widths[ZONE_FW_SEQ], tmpstr);
303
304                 if (desc->zone_flags & SRZ_ZONE_RESET)
305                         snprintf(tmpstr, sizeof(tmpstr), "Reset%cNeeded",
306                             word_sep);
307                 else
308                         snprintf(tmpstr, sizeof(tmpstr), "No%cReset%cNeeded",
309                             word_sep, word_sep);
310
311                 printf("%*s\n", field_widths[ZONE_FW_RESET], tmpstr);
312                 
313                 next_lba = start_lba + length;
314         }
315 bailout:
316         *next_start_lba = next_lba;
317
318         return (status);
319 }
320
321 int
322 zone(struct cam_device *device, int argc, char **argv, char *combinedopt,
323      int task_attr, int retry_count, int timeout, int verbosemode __unused)
324 {
325         union ccb *ccb = NULL;
326         int action = -1, rep_option = -1;
327         int all_zones = 0;
328         uint64_t lba = 0;
329         int error = 0;
330         uint8_t *data_ptr = NULL;
331         uint32_t alloc_len = 65536, valid_len = 0;
332         camcontrol_devtype devtype;
333         int ata_format = 0, use_ncq = 0;
334         int first_pass = 1;
335         zone_print_status zp_status;
336         zone_output_flags out_flags = ZONE_OF_NORMAL;
337         uint8_t *cdb_storage = NULL;
338         int cdb_storage_len = 32;
339         int c;
340
341         ccb = cam_getccb(device);
342         if (ccb == NULL) {
343                 warnx("%s: error allocating CCB", __func__);
344                 error = 1;
345                 goto bailout;
346         }
347
348         while ((c = getopt(argc, argv, combinedopt)) != -1) {
349                 switch (c) {
350                 case 'a':
351                         all_zones = 1;
352                         break;
353                 case 'c': {
354                         scsi_nv_status status;
355                         int entry_num;
356
357                         status = scsi_get_nv(zone_cmd_map,
358                             (sizeof(zone_cmd_map) / sizeof(zone_cmd_map[0])),
359                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
360                         if (status == SCSI_NV_FOUND)
361                                 action = zone_cmd_map[entry_num].value;
362                         else {
363                                 warnx("%s: %s: %s option %s", __func__,
364                                     (status == SCSI_NV_AMBIGUOUS) ?
365                                     "ambiguous" : "invalid", "zone command",
366                                     optarg);
367                                 error = 1;
368                                 goto bailout;
369                         }
370                         break;
371                 }
372                 case 'l': {
373                         char *endptr;
374
375                         lba = strtoull(optarg, &endptr, 0);
376                         if (*endptr != '\0') {
377                                 warnx("%s: invalid lba argument %s", __func__,
378                                     optarg);
379                                 error = 1;
380                                 goto bailout;
381                         }
382                         break;
383                 }
384                 case 'N':
385                         use_ncq = 1;
386                         break;
387                 case 'o': {
388                         scsi_nv_status status;
389                         int entry_num;
390
391                         status = scsi_get_nv(zone_rep_opts,
392                             (sizeof(zone_rep_opts) /sizeof(zone_rep_opts[0])),
393                             optarg, &entry_num, SCSI_NV_FLAG_IG_CASE);
394                         if (status == SCSI_NV_FOUND)
395                                 rep_option = zone_rep_opts[entry_num].value;
396                         else {
397                                 warnx("%s: %s: %s option %s", __func__,
398                                     (status == SCSI_NV_AMBIGUOUS) ?
399                                     "ambiguous" : "invalid", "report zones",
400                                     optarg);
401                                 error = 1;
402                                 goto bailout;
403                         }
404                         break;
405                 }
406                 case 'P': {
407                         scsi_nv_status status;
408                         int entry_num;
409
410                         status = scsi_get_nv(zone_print_opts,
411                             (sizeof(zone_print_opts) /
412                             sizeof(zone_print_opts[0])), optarg, &entry_num,
413                             SCSI_NV_FLAG_IG_CASE);
414                         if (status == SCSI_NV_FOUND)
415                                 out_flags = zone_print_opts[entry_num].value;
416                         else {
417                                 warnx("%s: %s: %s option %s", __func__,
418                                     (status == SCSI_NV_AMBIGUOUS) ?
419                                     "ambiguous" : "invalid", "print",
420                                     optarg);
421                                 error = 1;
422                                 goto bailout;
423                         }
424                         break;
425                 }
426                 default:
427                         break;
428                 }
429         }
430         if (action == -1) {
431                 warnx("%s: must specify -c <zone_cmd>", __func__);
432                 error = 1;
433                 goto bailout;
434         }
435         error = get_device_type(device, retry_count, timeout,
436             /*printerrors*/ 1, &devtype);
437         if (error != 0)
438                 errx(1, "Unable to determine device type");
439
440         if (action == ZBC_IN_SA_REPORT_ZONES) {
441
442                 data_ptr = malloc(alloc_len);
443                 if (data_ptr == NULL)
444                         err(1, "unable to allocate %u bytes", alloc_len);
445
446 restart_report:
447                 bzero(data_ptr, alloc_len);
448                 
449                 switch (devtype) {
450                 case CC_DT_SCSI:
451                         scsi_zbc_in(&ccb->csio,
452                             /*retries*/ retry_count,
453                             /*cbfcnp*/ NULL,
454                             /*tag_action*/ task_attr,
455                             /*service_action*/ action,
456                             /*zone_start_lba*/ lba,
457                             /*zone_options*/ (rep_option != -1) ?
458                                               rep_option : 0,
459                             /*data_ptr*/ data_ptr,
460                             /*dxfer_len*/ alloc_len,
461                             /*sense_len*/ SSD_FULL_SIZE,
462                             /*timeout*/ timeout ? timeout : 60000);
463                         break;
464                 case CC_DT_ATA:
465                 case CC_DT_SATL: {
466                         uint8_t command = 0;
467                         uint8_t protocol = 0;
468                         uint16_t features = 0, sector_count = 0;
469                         uint32_t auxiliary = 0;
470
471                         /*
472                          * XXX KDM support the partial bit?
473                          */
474                         if (use_ncq == 0) {
475                                 command = ATA_ZAC_MANAGEMENT_IN;
476                                 features = action;
477                                 if (rep_option != -1)
478                                         features |= (rep_option << 8);  
479                                 sector_count = ZAC_ATA_SECTOR_COUNT(alloc_len);
480                                 protocol = AP_PROTO_DMA;
481                         } else {
482                                 if (cdb_storage == NULL)
483                                         cdb_storage = calloc(cdb_storage_len, 1);
484                                 if (cdb_storage == NULL)
485                                         err(1, "couldn't allocate memory");
486
487                                 command = ATA_RECV_FPDMA_QUEUED;
488                                 features = ZAC_ATA_SECTOR_COUNT(alloc_len);
489                                 sector_count = ATA_RFPDMA_ZAC_MGMT_IN << 8;
490                                 auxiliary = action & 0xf;
491                                 if (rep_option != -1)
492                                         auxiliary |= rep_option << 8;
493                                 protocol = AP_PROTO_FPDMA;
494                         }
495
496                         error = build_ata_cmd(ccb,
497                             /*retry_count*/ retry_count,
498                             /*flags*/ CAM_DIR_IN | CAM_DEV_QFRZDIS,
499                             /*tag_action*/ task_attr,
500                             /*protocol*/ protocol,
501                             /*ata_flags*/ AP_FLAG_BYT_BLOK_BLOCKS |
502                                           AP_FLAG_TLEN_SECT_CNT |
503                                           AP_FLAG_TDIR_FROM_DEV,
504                             /*features*/ features,
505                             /*sector_count*/ sector_count,
506                             /*lba*/ lba,
507                             /*command*/ command,
508                             /*auxiliary*/ auxiliary,
509                             /*data_ptr*/ data_ptr,
510                             /*dxfer_len*/ ZAC_ATA_SECTOR_COUNT(alloc_len)*512,
511                             /*cdb_storage*/ cdb_storage,
512                             /*cdb_storage_len*/ cdb_storage_len,
513                             /*sense_len*/ SSD_FULL_SIZE,
514                             /*timeout*/ timeout ? timeout : 60000,
515                             /*is48bit*/ 1,
516                             /*devtype*/ devtype);
517
518                         if (error != 0) {
519                                 warnx("%s: build_ata_cmd() failed, likely "
520                                     "programmer error", __func__);
521                                 goto bailout;
522                         }
523
524                         ata_format = 1;
525
526                         break;
527                 }
528                 default:
529                         warnx("%s: Unknown device type %d", __func__,devtype);
530                         error = 1;
531                         goto bailout;
532                         break; /*NOTREACHED*/
533                 }
534         } else {
535                 /*
536                  * XXX KDM the current methodology is to always send ATA
537                  * commands to ATA devices.  Need to figure out how to
538                  * detect whether a SCSI to ATA translation layer will
539                  * translate ZBC IN/OUT commands to the appropriate ZAC
540                  * command.
541                  */
542                 switch (devtype) {
543                 case CC_DT_SCSI:
544                         scsi_zbc_out(&ccb->csio,
545                             /*retries*/ retry_count,
546                             /*cbfcnp*/ NULL,
547                             /*tag_action*/ task_attr,
548                             /*service_action*/ action,
549                             /*zone_id*/ lba,
550                             /*zone_flags*/ (all_zones != 0) ? ZBC_OUT_ALL : 0,
551                             /*data_ptr*/ NULL,
552                             /*dxfer_len*/ 0,
553                             /*sense_len*/ SSD_FULL_SIZE,
554                             /*timeout*/ timeout ? timeout : 60000);
555                         break;
556                 case CC_DT_ATA:
557                 case CC_DT_SATL: {
558                         uint8_t command = 0;
559                         uint8_t protocol = 0;
560                         uint16_t features = 0, sector_count = 0;
561                         uint32_t auxiliary = 0;
562
563                         /*
564                          * Note that we're taking advantage of the fact
565                          * that the action numbers are the same between the
566                          * ZBC and ZAC specs.
567                          */
568
569                         if (use_ncq == 0) {
570                                 protocol = AP_PROTO_NON_DATA;
571                                 command = ATA_ZAC_MANAGEMENT_OUT;
572                                 features = action & 0xf;
573                                 if (all_zones != 0)
574                                         features |= (ZBC_OUT_ALL << 8);
575                         } else {
576                                 cdb_storage = calloc(cdb_storage_len, 1);
577                                 if (cdb_storage == NULL)
578                                         err(1, "couldn't allocate memory");
579
580                                 protocol = AP_PROTO_FPDMA;
581                                 command = ATA_NCQ_NON_DATA;
582                                 features = ATA_NCQ_ZAC_MGMT_OUT;
583                                 auxiliary = action & 0xf;
584                                 if (all_zones != 0)
585                                         auxiliary |= (ZBC_OUT_ALL << 8);
586                         }
587
588
589                         error = build_ata_cmd(ccb,
590                             /*retry_count*/ retry_count,
591                             /*flags*/ CAM_DIR_NONE | CAM_DEV_QFRZDIS,
592                             /*tag_action*/ task_attr,
593                             /*protocol*/ protocol,
594                             /*ata_flags*/ AP_FLAG_BYT_BLOK_BYTES |
595                                           AP_FLAG_TLEN_NO_DATA,
596                             /*features*/ features,
597                             /*sector_count*/ sector_count,
598                             /*lba*/ lba,
599                             /*command*/ command,
600                             /*auxiliary*/ auxiliary,
601                             /*data_ptr*/ NULL,
602                             /*dxfer_len*/ 0,
603                             /*cdb_storage*/ cdb_storage,
604                             /*cdb_storage_len*/ cdb_storage_len,
605                             /*sense_len*/ SSD_FULL_SIZE,
606                             /*timeout*/ timeout ? timeout : 60000,
607                             /*is48bit*/ 1,
608                             /*devtype*/ devtype);
609                         if (error != 0) {
610                                 warnx("%s: build_ata_cmd() failed, likely "
611                                     "programmer error", __func__);
612                                 goto bailout;
613                         }
614                         ata_format = 1;
615                         break;
616                 }
617                 default:
618                         warnx("%s: Unknown device type %d", __func__,devtype);
619                         error = 1;
620                         goto bailout;
621                         break; /*NOTREACHED*/
622                 }
623         }
624
625         ccb->ccb_h.flags |= CAM_DEV_QFRZDIS;
626         if (retry_count > 0)
627                 ccb->ccb_h.flags |= CAM_PASS_ERR_RECOVER;
628
629         error = cam_send_ccb(device, ccb);
630         if (error != 0) {
631                 warn("error sending %s %s CCB", (devtype == CC_DT_SCSI) ?
632                      "ZBC" : "ZAC Management",
633                      (action == ZBC_IN_SA_REPORT_ZONES) ? "In" : "Out");
634                 error = -1;
635                 goto bailout;
636         }
637
638         if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) {
639                 cam_error_print(device, ccb, CAM_ESF_ALL, CAM_EPF_ALL,stderr);
640                 error = 1;
641                 goto bailout;
642         }
643
644         /*
645          * If we aren't reading the list of zones, we're done.
646          */
647         if (action != ZBC_IN_SA_REPORT_ZONES)
648                 goto bailout;
649
650         if (ccb->ccb_h.func_code == XPT_SCSI_IO)
651                 valid_len = ccb->csio.dxfer_len - ccb->csio.resid;
652         else
653                 valid_len = ccb->ataio.dxfer_len - ccb->ataio.resid;
654
655         zp_status = zone_rz_print(data_ptr, valid_len, ata_format, out_flags,
656             first_pass, &lba);
657
658         if (zp_status == ZONE_PRINT_MORE_DATA) {
659                 bzero(ccb, sizeof(*ccb));
660                 first_pass = 0;
661                 if (cdb_storage != NULL)
662                         bzero(cdb_storage, cdb_storage_len);
663                 goto restart_report;
664         } else if (zp_status == ZONE_PRINT_ERROR)
665                 error = 1;
666 bailout:
667         if (ccb != NULL)
668                 cam_freeccb(ccb);
669
670         free(data_ptr);
671         free(cdb_storage);
672
673         return (error);
674 }