]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - lib/libcam/scsi_cmdparse.c
MFC r335669:
[FreeBSD/stable/10.git] / lib / libcam / scsi_cmdparse.c
1 /*
2  * Taken from the original FreeBSD user SCSI library.
3  */
4 /* Copyright (c) 1994 HD Associates
5  * (contact: dufault@hda.com)
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  * This product includes software developed by HD Associates
19  * 4. Neither the name of the HD Associaates nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
35  */
36
37 #include <sys/cdefs.h>
38 __FBSDID("$FreeBSD$");
39
40 #include <sys/types.h>
41
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <ctype.h>
45 #include <string.h>
46 #include <sys/errno.h>
47 #include <stdarg.h>
48 #include <fcntl.h>
49
50 #include <cam/cam.h>
51 #include <cam/cam_ccb.h>
52 #include <cam/scsi/scsi_message.h>
53 #include "camlib.h"
54
55 /*
56  * Decode: Decode the data section of a scsireq.  This decodes
57  * trivial grammar:
58  *
59  * fields : field fields
60  *        ;
61  *
62  * field : field_specifier
63  *       | control
64  *       ;
65  *
66  * control : 's' seek_value
67  *       | 's' '+' seek_value
68  *       ;
69  *
70  * seek_value : DECIMAL_NUMBER
71  *       | 'v'                          // For indirect seek, i.e., value from the arg list
72  *       ;
73  *
74  * field_specifier : type_specifier field_width
75  *       | '{' NAME '}' type_specifier field_width
76  *       ;
77  *
78  * field_width : DECIMAL_NUMBER
79  *       ;
80  *
81  * type_specifier : 'i' // Integral types (i1, i2, i3, i4)
82  *       | 'b'                          // Bits
83  *       | 't'                          // Bits
84  *       | 'c'                          // Character arrays
85  *       | 'z'                          // Character arrays with zeroed trailing spaces
86  *       ;
87  *
88  * Notes:
89  * 1. Integral types are swapped into host order.
90  * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation.
91  * 3. 's' permits "seeking" in the string.  "s+DECIMAL" seeks relative to
92  *    DECIMAL; "sDECIMAL" seeks absolute to decimal.
93  * 4. 's' permits an indirect reference.  "sv" or "s+v" will get the
94  *    next integer value from the arg array.
95  * 5. Field names can be anything between the braces
96  *
97  * BUGS:
98  * i and b types are promoted to ints.
99  *
100  */
101
102 static int
103 do_buff_decode(u_int8_t *buff, size_t len,
104                void (*arg_put)(void *, int , void *, int, char *),
105                void *puthook, const char *fmt, va_list *ap)
106 {
107         int ind = 0;
108         int assigned = 0;
109         int width;
110         int suppress;
111         int plus;
112         int done = 0;
113         static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
114                                    0x1f, 0x3f, 0x7f, 0xff};
115         int value;
116         char *intendp;
117         char letter;
118         char field_name[80];
119
120 #define ARG_PUT(ARG) \
121         do { \
122                 if (!suppress) { \
123                         if (arg_put) \
124                                 (*arg_put)(puthook, (letter == 't' ? 'b' : \
125                                     letter), (void *)((long)(ARG)), width, \
126                                     field_name); \
127                         else \
128                                 *(va_arg(*ap, int *)) = (ARG); \
129                         assigned++; \
130                 } \
131                 field_name[0] = '\0'; \
132                 suppress = 0; \
133         } while (0)
134
135         u_char bits = 0;        /* For bit fields */
136         int shift = 0;          /* Bits already shifted out */
137         suppress = 0;
138         field_name[0] = '\0';
139
140         while (!done) {
141                 switch(letter = *fmt) {
142                 case ' ':       /* White space */
143                 case '\t':
144                 case '\r':
145                 case '\n':
146                 case '\f':
147                         fmt++;
148                         break;
149
150                 case '#':       /* Comment */
151                         while (*fmt && (*fmt != '\n'))
152                                 fmt++;
153                         if (fmt)
154                                 fmt++;  /* Skip '\n' */
155                         break;
156
157                 case '*':       /* Suppress assignment */
158                         fmt++;
159                         suppress = 1;
160                         break;
161
162                 case '{':       /* Field Name */
163                 {
164                         int i = 0;
165                         fmt++;  /* Skip '{' */
166                         while (*fmt && (*fmt != '}')) {
167                                 if (i < sizeof(field_name))
168                                         field_name[i++] = *fmt;
169
170                                 fmt++;
171                         }
172                         if (*fmt != '\0')
173                                 fmt++;  /* Skip '}' */
174                         field_name[i] = '\0';
175                         break;
176                 }
177
178                 case 't':       /* Bit (field) */
179                 case 'b':       /* Bits */
180                         fmt++;
181                         width = strtol(fmt, &intendp, 10);
182                         fmt = intendp;
183                         if (width > 8)
184                                 done = 1;
185                         else {
186                                 if (shift <= 0) {
187                                         if (ind >= len) {
188                                                 done = 1;
189                                                 break;
190                                         }
191                                         bits = buff[ind++];
192                                         shift = 8;
193                                 }
194                                 value = (bits >> (shift - width)) &
195                                          mask[width];
196
197 #if 0
198                                 printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
199                                 shift, bits, value, width, mask[width]);
200 #endif
201
202                                 ARG_PUT(value);
203
204                                 shift -= width;
205                         }
206                         break;
207
208                 case 'i':       /* Integral values */
209                         shift = 0;
210                         fmt++;
211                         width = strtol(fmt, &intendp, 10);
212                         fmt = intendp;
213                         if (ind + width > len) {
214                                 done = 1;
215                                 break;
216                         }
217                         switch(width) {
218                         case 1:
219                                 ARG_PUT(buff[ind]);
220                                 ind++;
221                                 break;
222
223                         case 2:
224                                 ARG_PUT(buff[ind] << 8 | buff[ind + 1]);
225                                 ind += 2;
226                                 break;
227
228                         case 3:
229                                 ARG_PUT(buff[ind] << 16 |
230                                         buff[ind + 1] << 8 | buff[ind + 2]);
231                                 ind += 3;
232                                 break;
233
234                         case 4:
235                                 ARG_PUT(buff[ind] << 24 | buff[ind + 1] << 16 |
236                                         buff[ind + 2] << 8 | buff[ind + 3]);
237                                 ind += 4;
238                                 break;
239
240                         default:
241                                 done = 1;
242                                 break;
243                         }
244
245                         break;
246
247                 case 'c':       /* Characters (i.e., not swapped) */
248                 case 'z':       /* Characters with zeroed trailing spaces */
249                         shift = 0;
250                         fmt++;
251                         width = strtol(fmt, &intendp, 10);
252                         fmt = intendp;
253                         if (ind + width > len) {
254                                 done = 1;
255                                 break;
256                         }
257                         if (!suppress) {
258                                 if (arg_put != NULL)
259                                         (*arg_put)(puthook,
260                                             (letter == 't' ? 'b' : letter),
261                                             &buff[ind], width, field_name);
262                                 else {
263                                         char *dest;
264                                         dest = va_arg(*ap, char *);
265                                         bcopy(&buff[ind], dest, width);
266                                         if (letter == 'z') {
267                                                 char *p;
268                                                 for (p = dest + width - 1;
269                                                     p >= dest && *p == ' ';
270                                                     p--)
271                                                         *p = '\0';
272                                         }
273                                 }
274                                 assigned++;
275                         }
276                         ind += width;
277                         field_name[0] = 0;
278                         suppress = 0;
279                         break;
280
281                 case 's':       /* Seek */
282                         shift = 0;
283                         fmt++;
284                         if (*fmt == '+') {
285                                 plus = 1;
286                                 fmt++;
287                         } else
288                                 plus = 0;
289
290                         if (tolower(*fmt) == 'v') {
291                                 /*
292                                  * You can't suppress a seek value.  You also
293                                  * can't have a variable seek when you are using
294                                  * "arg_put".
295                                  */
296                                 width = (arg_put) ? 0 : va_arg(*ap, int);
297                                 fmt++;
298                         } else {
299                                 width = strtol(fmt, &intendp, 10);
300                                 fmt = intendp;
301                         }
302
303                         if (plus)
304                                 ind += width;   /* Relative seek */
305                         else
306                                 ind = width;    /* Absolute seek */
307
308                         break;
309
310                 case 0:
311                         done = 1;
312                         break;
313
314                 default:
315                         fprintf(stderr, "Unknown letter in format: %c\n",
316                                 letter);
317                         fmt++;
318                         break;
319                 }
320         }
321
322         return (assigned);
323 }
324
325 /* next_field: Return the next field in a command specifier.  This
326  * builds up a SCSI command using this trivial grammar:
327  *
328  * fields : field fields
329  *        ;
330  *
331  * field : value
332  *       | value ':' field_width
333  *       ;
334  *
335  * field_width : digit
336  *       | 'i' digit            // i2 = 2 byte integer, i3 = 3 byte integer etc.
337  *       ;
338  *
339  * value : HEX_NUMBER
340  *       | 'v'                          // For indirection.
341  *       ;
342  *
343  * Notes:
344  *  Bit fields are specified MSB first to match the SCSI spec.
345  *
346  * Examples:
347  *  TUR: "0 0 0 0 0 0"
348  *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
349  *
350  * The function returns the value:
351  *  0: For reached end, with error_p set if an error was found
352  *  1: For valid stuff setup
353  *  2: For "v" was entered as the value (implies use varargs)
354  *
355  */
356
357 static int
358 next_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
359            int n_name, int *error_p, int *suppress_p)
360 {
361         const char *p = *pp;
362         char *intendp;
363
364         int something = 0;
365
366         enum {
367                 BETWEEN_FIELDS,
368                 START_FIELD,
369                 GET_FIELD,
370                 DONE,
371         } state;
372
373         int value = 0;
374         int field_size;         /* Default to byte field type... */
375         int field_width;        /* 1 byte wide */
376         int is_error = 0;
377         int suppress = 0;
378
379         field_size = 8;         /* Default to byte field type... */
380         *fmt = 'i';
381         field_width = 1;        /* 1 byte wide */
382         if (name != NULL)
383                 *name = '\0';
384
385         state = BETWEEN_FIELDS;
386
387         while (state != DONE) {
388                 switch(state) {
389                 case BETWEEN_FIELDS:
390                         if (*p == '\0')
391                                 state = DONE;
392                         else if (isspace(*p))
393                                 p++;
394                         else if (*p == '#') {
395                                 while (*p && *p != '\n')
396                                         p++;
397                                 if (*p != '\0')
398                                         p++;
399                         } else if (*p == '{') {
400                                 int i = 0;
401
402                                 p++;
403
404                                 while (*p && *p != '}') {
405                                         if(name && i < n_name) {
406                                                 name[i] = *p;
407                                                 i++;
408                                         }
409                                         p++;
410                                 }
411
412                                 if(name && i < n_name)
413                                         name[i] = '\0';
414
415                                 if (*p == '}')
416                                         p++;
417                         } else if (*p == '*') {
418                                 p++;
419                                 suppress = 1;
420                         } else if (isxdigit(*p)) {
421                                 something = 1;
422                                 value = strtol(p, &intendp, 16);
423                                 p = intendp;
424                                 state = START_FIELD;
425                         } else if (tolower(*p) == 'v') {
426                                 p++;
427                                 something = 2;
428                                 value = *value_p;
429                                 state = START_FIELD;
430                         } else if (tolower(*p) == 'i') {
431                                 /*
432                                  * Try to work without the "v".
433                                  */
434                                 something = 2;
435                                 value = *value_p;
436                                 p++;
437
438                                 *fmt = 'i';
439                                 field_size = 8;
440                                 field_width = strtol(p, &intendp, 10);
441                                 p = intendp;
442                                 state = DONE;
443
444                         } else if (tolower(*p) == 't') {
445                                 /*
446                                  * XXX: B can't work: Sees the 'b' as a
447                                  * hex digit in "isxdigit".  try "t" for
448                                  * bit field.
449                                  */
450                                 something = 2;
451                                 value = *value_p;
452                                 p++;
453
454                                 *fmt = 'b';
455                                 field_size = 1;
456                                 field_width = strtol(p, &intendp, 10);
457                                 p = intendp;
458                                 state = DONE;
459                         } else if (tolower(*p) == 's') {
460                                 /* Seek */
461                                 *fmt = 's';
462                                 p++;
463                                 if (tolower(*p) == 'v') {
464                                         p++;
465                                         something = 2;
466                                         value = *value_p;
467                                 } else {
468                                         something = 1;
469                                         value = strtol(p, &intendp, 0);
470                                         p = intendp;
471                                 }
472                                 state = DONE;
473                         } else {
474                                 fprintf(stderr, "Invalid starting "
475                                         "character: %c\n", *p);
476                                 is_error = 1;
477                                 state = DONE;
478                         }
479                         break;
480
481                 case START_FIELD:
482                         if (*p == ':') {
483                                 p++;
484                                 field_size = 1;         /* Default to bits
485                                                            when specified */
486                                 state = GET_FIELD;
487                         } else
488                                 state = DONE;
489                         break;
490
491                 case GET_FIELD:
492                         if (isdigit(*p)) {
493                                 *fmt = 'b';
494                                 field_size = 1;
495                                 field_width = strtol(p, &intendp, 10);
496                                 p = intendp;
497                                 state = DONE;
498                         } else if (*p == 'i') {
499
500                                 /* Integral (bytes) */
501                                 p++;
502
503                                 *fmt = 'i';
504                                 field_size = 8;
505                                 field_width = strtol(p, &intendp, 10);
506                                 p = intendp;
507                                 state = DONE;
508                         } else if (*p == 'b') {
509
510                                 /* Bits */
511                                 p++;
512
513                                 *fmt = 'b';
514                                 field_size = 1;
515                                 field_width = strtol(p, &intendp, 10);
516                                 p = intendp;
517                                 state = DONE;
518                         } else {
519                                 fprintf(stderr, "Invalid startfield %c "
520                                         "(%02x)\n", *p, *p);
521                                 is_error = 1;
522                                 state = DONE;
523                         }
524                         break;
525
526                 case DONE:
527                         break;
528                 }
529         }
530
531         if (is_error) {
532                 *error_p = 1;
533                 return (0);
534         }
535
536         *error_p = 0;
537         *pp = p;
538         *width_p = field_width * field_size;
539         *value_p = value;
540         *suppress_p = suppress;
541
542         return (something);
543 }
544
545 static int
546 do_encode(u_char *buff, size_t vec_max, size_t *used,
547           int (*arg_get)(void *, char *), void *gethook, const char *fmt,
548           va_list *ap)
549 {
550         int ind;
551         int shift;
552         u_char val;
553         int ret;
554         int width, value, error, suppress;
555         char c;
556         int encoded = 0;
557         char field_name[80];
558
559         ind = 0;
560         shift = 0;
561         val = 0;
562
563         while ((ret = next_field(&fmt, &c, &width, &value, field_name,
564                                  sizeof(field_name), &error, &suppress))) {
565                 encoded++;
566
567                 if (ret == 2) {
568                         if (suppress)
569                                 value = 0;
570                         else
571                                 value = arg_get != NULL ?
572                                         (*arg_get)(gethook, field_name) :
573                                         va_arg(*ap, int);
574                 }
575
576 #if 0
577                 printf(
578 "do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
579                 ret, c, width, value, field_name, error, suppress);
580 #endif
581                 /* Absolute seek */
582                 if (c == 's') {
583                         ind = value;
584                         continue;
585                 }
586
587                 /* A width of < 8 is a bit field. */
588                 if (width < 8) {
589
590                         /* This is a bit field.  We start with the high bits
591                          * so it reads the same as the SCSI spec.
592                          */
593
594                         shift += width;
595
596                         val |= (value << (8 - shift));
597
598                         if (shift == 8) {
599                                 if (ind < vec_max) {
600                                         buff[ind++] = val;
601                                         val = 0;
602                                 }
603                                 shift = 0;
604                         }
605                 } else {
606                         if (shift) {
607                                 if (ind < vec_max) {
608                                         buff[ind++] = val;
609                                         val = 0;
610                                 }
611                                 shift = 0;
612                         }
613                         switch(width) {
614                         case 8:         /* 1 byte integer */
615                                 if (ind < vec_max)
616                                         buff[ind++] = value;
617                                 break;
618
619                         case 16:        /* 2 byte integer */
620                                 if (ind < vec_max - 2 + 1) {
621                                         buff[ind++] = value >> 8;
622                                         buff[ind++] = value;
623                                 }
624                                 break;
625
626                         case 24:        /* 3 byte integer */
627                                 if (ind < vec_max - 3 + 1) {
628                                         buff[ind++] = value >> 16;
629                                         buff[ind++] = value >> 8;
630                                         buff[ind++] = value;
631                                 }
632                                 break;
633
634                         case 32:        /* 4 byte integer */
635                                 if (ind < vec_max - 4 + 1) {
636                                         buff[ind++] = value >> 24;
637                                         buff[ind++] = value >> 16;
638                                         buff[ind++] = value >> 8;
639                                         buff[ind++] = value;
640                                 }
641                                 break;
642
643                         default:
644                                 fprintf(stderr, "do_encode: Illegal width\n");
645                                 break;
646                         }
647                 }
648         }
649
650         /* Flush out any remaining bits
651          */
652         if (shift && ind < vec_max) {
653                 buff[ind++] = val;
654                 val = 0;
655         }
656
657
658         if (used)
659                 *used = ind;
660
661         if (error)
662                 return (-1);
663
664         return (encoded);
665 }
666
667 int
668 csio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
669 {
670         va_list ap;
671         int retval;
672
673         va_start(ap, fmt);
674
675         retval = do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
676             NULL, NULL, fmt, &ap);
677
678         va_end(ap);
679
680         return (retval);
681 }
682
683 int
684 csio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
685                   void (*arg_put)(void *, int, void *, int, char *),
686                   void *puthook)
687 {
688
689         /*
690          * We need some way to output things; we can't do it without
691          * the arg_put function.
692          */
693         if (arg_put == NULL)
694                 return (-1);
695
696         return (do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
697                     arg_put, puthook, fmt, NULL));
698 }
699
700 int
701 buff_decode(u_int8_t *buff, size_t len, const char *fmt, ...)
702 {
703         va_list ap;
704         int retval;
705
706         va_start(ap, fmt);
707
708         retval = do_buff_decode(buff, len, NULL, NULL, fmt, &ap);
709
710         va_end(ap);
711
712         return (retval);
713 }
714
715 int
716 buff_decode_visit(u_int8_t *buff, size_t len, const char *fmt,
717                   void (*arg_put)(void *, int, void *, int, char *),
718                   void *puthook)
719 {
720
721         /*
722          * We need some way to output things; we can't do it without
723          * the arg_put function.
724          */
725         if (arg_put == NULL)
726                 return (-1);
727
728         return (do_buff_decode(buff, len, arg_put, puthook, fmt, NULL));
729 }
730
731 /*
732  * Build a SCSI CCB, given the command and data pointers and a format
733  * string describing the 
734  */
735 int
736 csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
737            u_int32_t flags, int retry_count, int timeout, const char *cmd_spec,
738            ...)
739 {
740         size_t cmdlen;
741         int retval;
742         va_list ap;
743
744         if (csio == NULL)
745                 return (0);
746
747         bzero(csio, sizeof(struct ccb_scsiio));
748
749         va_start(ap, cmd_spec);
750
751         if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
752                                 &cmdlen, NULL, NULL, cmd_spec, &ap)) == -1)
753                 goto done;
754
755         cam_fill_csio(csio,
756                       /* retries */ retry_count,
757                       /* cbfcnp */ NULL,
758                       /* flags */ flags,
759                       /* tag_action */ MSG_SIMPLE_Q_TAG,
760                       /* data_ptr */ data_ptr,
761                       /* dxfer_len */ dxfer_len,
762                       /* sense_len */ SSD_FULL_SIZE,
763                       /* cdb_len */ cmdlen,
764                       /* timeout */ timeout ? timeout : 5000);
765
766 done:
767         va_end(ap);
768
769         return (retval);
770 }
771
772 int
773 csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
774                  u_int32_t dxfer_len, u_int32_t flags, int retry_count,
775                  int timeout, const char *cmd_spec,
776                  int (*arg_get)(void *hook, char *field_name), void *gethook)
777 {
778         size_t cmdlen;
779         int retval;
780
781         if (csio == NULL)
782                 return (0);
783
784         /*
785          * We need something to encode, but we can't get it without the
786          * arg_get function.
787          */
788         if (arg_get == NULL)
789                 return (-1);
790
791         bzero(csio, sizeof(struct ccb_scsiio));
792
793         if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
794                                 &cmdlen, arg_get, gethook, cmd_spec, NULL)) == -1)
795                 return (retval);
796
797         cam_fill_csio(csio,
798                       /* retries */ retry_count,
799                       /* cbfcnp */ NULL,
800                       /* flags */ flags,
801                       /* tag_action */ MSG_SIMPLE_Q_TAG,
802                       /* data_ptr */ data_ptr,
803                       /* dxfer_len */ dxfer_len,
804                       /* sense_len */ SSD_FULL_SIZE,
805                       /* cdb_len */ cmdlen,
806                       /* timeout */ timeout ? timeout : 5000);
807
808         return (retval);
809 }
810
811 int
812 csio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
813 {
814         va_list ap;
815         int retval;
816
817         if (csio == NULL)
818                 return (0);
819
820         va_start(ap, fmt);
821
822         retval = do_encode(csio->data_ptr, csio->dxfer_len, NULL, NULL, NULL,
823             fmt, &ap);
824
825         va_end(ap);
826
827         return (retval);
828 }
829
830 int
831 buff_encode_visit(u_int8_t *buff, size_t len, const char *fmt,
832                   int (*arg_get)(void *hook, char *field_name), void *gethook)
833 {
834
835         /*
836          * We need something to encode, but we can't get it without the
837          * arg_get function.
838          */
839         if (arg_get == NULL)
840                 return (-1);
841
842         return (do_encode(buff, len, NULL, arg_get, gethook, fmt, NULL));
843 }
844
845 int
846 csio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
847                   int (*arg_get)(void *hook, char *field_name), void *gethook)
848 {
849
850         /*
851          * We need something to encode, but we can't get it without the
852          * arg_get function.
853          */
854         if (arg_get == NULL)
855                 return (-1);
856
857         return (do_encode(csio->data_ptr, csio->dxfer_len, NULL, arg_get,
858                          gethook, fmt, NULL));
859 }