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