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
do_buff_decode(u_int8_t * databuf,size_t len,void (* arg_put)(void *,int,void *,int,char *),void * puthook,const char * fmt,va_list ap)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
next_field(const char ** pp,char * fmt,int * width_p,int * value_p,char * name,int n_name,int * error_p,int * suppress_p)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
do_encode(u_char * buff,size_t vec_max,size_t * used,int (* arg_get)(void *,char *),void * gethook,const char * fmt,va_list ap)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
csio_decode(struct ccb_scsiio * csio,const char * fmt,...)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
csio_decode_visit(struct ccb_scsiio * csio,const char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)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
buff_decode(u_int8_t * buff,size_t len,const char * fmt,...)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
buff_decode_visit(u_int8_t * buff,size_t len,const char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)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
csio_build(struct ccb_scsiio * csio,u_int8_t * data_ptr,u_int32_t dxfer_len,u_int32_t flags,int retry_count,int timeout,const char * cmd_spec,...)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
csio_build_visit(struct ccb_scsiio * csio,u_int8_t * data_ptr,u_int32_t dxfer_len,u_int32_t flags,int retry_count,int timeout,const char * cmd_spec,int (* arg_get)(void * hook,char * field_name),void * gethook)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
csio_encode(struct ccb_scsiio * csio,const char * fmt,...)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
buff_encode_visit(u_int8_t * buff,size_t len,const char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)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
csio_encode_visit(struct ccb_scsiio * csio,const char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)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 }
850