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