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