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