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