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