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