xref: /freebsd/lib/libcam/scsi_cmdparse.c (revision 4b9d6057)
1 /*
2  * Taken from the original FreeBSD user SCSI library.
3  */
4 /*-
5  * SPDX-License-Identifier: BSD-4-Clause
6  *
7  * Copyright (c) 1994 HD Associates
8  * (contact: dufault@hda.com)
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  * This product includes software developed by HD Associates
22  * 4. Neither the name of the HD Associaates nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  * From: scsi.c,v 1.8 1997/02/22 15:07:54 peter Exp $
38  */
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
103 do_buff_decode(uint8_t *buff, size_t len,
104 	       void (*arg_put)(void *, int , void *, int, char *),
105 	       void *puthook, const char *fmt, va_list *ap)
106 {
107 	int ind = 0;
108 	int assigned = 0;
109 	int width;
110 	int suppress;
111 	int plus;
112 	int done = 0;
113 	static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f,
114 				   0x1f, 0x3f, 0x7f, 0xff};
115 	int value;
116 	char *intendp;
117 	char letter;
118 	char field_name[80];
119 
120 #define ARG_PUT(ARG) \
121 	do { \
122 		if (!suppress) { \
123 			if (arg_put) \
124 				(*arg_put)(puthook, (letter == 't' ? 'b' : \
125 				    letter), (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 != '\0')
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, &intendp, 10);
182 			fmt = intendp;
183 			if (width > 8)
184 				done = 1;
185 			else {
186 				if (shift <= 0) {
187 					if (ind >= len) {
188 						done = 1;
189 						break;
190 					}
191 					bits = buff[ind++];
192 					shift = 8;
193 				}
194 				value = (bits >> (shift - width)) &
195 					 mask[width];
196 
197 #if 0
198 				printf("shift %2d bits %02x value %02x width %2d mask %02x\n",
199 				shift, bits, value, width, mask[width]);
200 #endif
201 
202 				ARG_PUT(value);
203 
204 				shift -= width;
205 			}
206 			break;
207 
208 		case 'i':	/* Integral values */
209 			shift = 0;
210 			fmt++;
211 			width = strtol(fmt, &intendp, 10);
212 			fmt = intendp;
213 			if (ind + width > len) {
214 				done = 1;
215 				break;
216 			}
217 			switch(width) {
218 			case 1:
219 				ARG_PUT(buff[ind]);
220 				ind++;
221 				break;
222 
223 			case 2:
224 				ARG_PUT(buff[ind] << 8 | buff[ind + 1]);
225 				ind += 2;
226 				break;
227 
228 			case 3:
229 				ARG_PUT(buff[ind] << 16 |
230 					buff[ind + 1] << 8 | buff[ind + 2]);
231 				ind += 3;
232 				break;
233 
234 			case 4:
235 				ARG_PUT(buff[ind] << 24 | buff[ind + 1] << 16 |
236 					buff[ind + 2] << 8 | buff[ind + 3]);
237 				ind += 4;
238 				break;
239 
240 			default:
241 				done = 1;
242 				break;
243 			}
244 
245 			break;
246 
247 		case 'c':	/* Characters (i.e., not swapped) */
248 		case 'z':	/* Characters with zeroed trailing spaces */
249 			shift = 0;
250 			fmt++;
251 			width = strtol(fmt, &intendp, 10);
252 			fmt = intendp;
253 			if (ind + width > len) {
254 				done = 1;
255 				break;
256 			}
257 			if (!suppress) {
258 				if (arg_put != NULL)
259 					(*arg_put)(puthook,
260 					    (letter == 't' ? 'b' : letter),
261 					    &buff[ind], width, field_name);
262 				else {
263 					char *dest;
264 					dest = va_arg(*ap, char *);
265 					bcopy(&buff[ind], dest, width);
266 					if (letter == 'z') {
267 						char *p;
268 						for (p = dest + width - 1;
269 						    p >= dest && *p == ' ';
270 						    p--)
271 							*p = '\0';
272 					}
273 				}
274 				assigned++;
275 			}
276 			ind += width;
277 			field_name[0] = 0;
278 			suppress = 0;
279 			break;
280 
281 		case 's':	/* Seek */
282 			shift = 0;
283 			fmt++;
284 			if (*fmt == '+') {
285 				plus = 1;
286 				fmt++;
287 			} else
288 				plus = 0;
289 
290 			if (tolower(*fmt) == 'v') {
291 				/*
292 				 * You can't suppress a seek value.  You also
293 				 * can't have a variable seek when you are using
294 				 * "arg_put".
295 				 */
296 				width = (arg_put) ? 0 : va_arg(*ap, int);
297 				fmt++;
298 			} else {
299 				width = strtol(fmt, &intendp, 10);
300 				fmt = intendp;
301 			}
302 
303 			if (plus)
304 				ind += width;	/* Relative seek */
305 			else
306 				ind = width;	/* Absolute seek */
307 
308 			break;
309 
310 		case 0:
311 			done = 1;
312 			break;
313 
314 		default:
315 			fprintf(stderr, "Unknown letter in format: %c\n",
316 				letter);
317 			fmt++;
318 			break;
319 		}
320 	}
321 
322 	return (assigned);
323 }
324 
325 /* next_field: Return the next field in a command specifier.  This
326  * builds up a SCSI command using this trivial grammar:
327  *
328  * fields : field fields
329  *        ;
330  *
331  * field : value
332  *       | value ':' field_width
333  *       ;
334  *
335  * field_width : digit
336  *       | 'i' digit		// i2 = 2 byte integer, i3 = 3 byte integer etc.
337  *       ;
338  *
339  * value : HEX_NUMBER
340  *       | 'v'				// For indirection.
341  *       ;
342  *
343  * Notes:
344  *  Bit fields are specified MSB first to match the SCSI spec.
345  *
346  * Examples:
347  *  TUR: "0 0 0 0 0 0"
348  *  WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length
349  *
350  * The function returns the value:
351  *  0: For reached end, with error_p set if an error was found
352  *  1: For valid stuff setup
353  *  2: For "v" was entered as the value (implies use varargs)
354  *
355  */
356 
357 static int
358 next_field(const char **pp, char *fmt, int *width_p, int *value_p, char *name,
359 	   int n_name, int *error_p, int *suppress_p)
360 {
361 	const char *p = *pp;
362 	char *intendp;
363 
364 	int something = 0;
365 
366 	enum {
367 		BETWEEN_FIELDS,
368 		START_FIELD,
369 		GET_FIELD,
370 		DONE,
371 	} state;
372 
373 	int value = 0;
374 	int field_size;		/* Default to byte field type... */
375 	int field_width;	/* 1 byte wide */
376 	int is_error = 0;
377 	int suppress = 0;
378 
379 	field_size = 8;		/* Default to byte field type... */
380 	*fmt = 'i';
381 	field_width = 1;	/* 1 byte wide */
382 	if (name != NULL)
383 		*name = '\0';
384 
385 	state = BETWEEN_FIELDS;
386 
387 	while (state != DONE) {
388 		switch(state) {
389 		case BETWEEN_FIELDS:
390 			if (*p == '\0')
391 				state = DONE;
392 			else if (isspace(*p))
393 				p++;
394 			else if (*p == '#') {
395 				while (*p && *p != '\n')
396 					p++;
397 				if (*p != '\0')
398 					p++;
399 			} else if (*p == '{') {
400 				int i = 0;
401 
402 				p++;
403 
404 				while (*p && *p != '}') {
405 					if(name && i < n_name) {
406 						name[i] = *p;
407 						i++;
408 					}
409 					p++;
410 				}
411 
412 				if(name && i < n_name)
413 					name[i] = '\0';
414 
415 				if (*p == '}')
416 					p++;
417 			} else if (*p == '*') {
418 				p++;
419 				suppress = 1;
420 			} else if (isxdigit(*p)) {
421 				something = 1;
422 				value = strtol(p, &intendp, 16);
423 				p = intendp;
424 				state = START_FIELD;
425 			} else if (tolower(*p) == 'v') {
426 				p++;
427 				something = 2;
428 				value = *value_p;
429 				state = START_FIELD;
430 			} else if (tolower(*p) == 'i') {
431 				/*
432 				 * Try to work without the "v".
433 				 */
434 				something = 2;
435 				value = *value_p;
436 				p++;
437 
438 				*fmt = 'i';
439 				field_size = 8;
440 				field_width = strtol(p, &intendp, 10);
441 				p = intendp;
442 				state = DONE;
443 
444 			} else if (tolower(*p) == 't') {
445 				/*
446 				 * XXX: B can't work: Sees the 'b' as a
447 				 * hex digit in "isxdigit".  try "t" for
448 				 * bit field.
449 				 */
450 				something = 2;
451 				value = *value_p;
452 				p++;
453 
454 				*fmt = 'b';
455 				field_size = 1;
456 				field_width = strtol(p, &intendp, 10);
457 				p = intendp;
458 				state = DONE;
459 			} else if (tolower(*p) == 's') {
460 				/* Seek */
461 				*fmt = 's';
462 				p++;
463 				if (tolower(*p) == 'v') {
464 					p++;
465 					something = 2;
466 					value = *value_p;
467 				} else {
468 					something = 1;
469 					value = strtol(p, &intendp, 0);
470 					p = intendp;
471 				}
472 				state = DONE;
473 			} else {
474 				fprintf(stderr, "Invalid starting "
475 					"character: %c\n", *p);
476 				is_error = 1;
477 				state = DONE;
478 			}
479 			break;
480 
481 		case START_FIELD:
482 			if (*p == ':') {
483 				p++;
484 				field_size = 1;		/* Default to bits
485 							   when specified */
486 				state = GET_FIELD;
487 			} else
488 				state = DONE;
489 			break;
490 
491 		case GET_FIELD:
492 			if (isdigit(*p)) {
493 				*fmt = 'b';
494 				field_size = 1;
495 				field_width = strtol(p, &intendp, 10);
496 				p = intendp;
497 				state = DONE;
498 			} else if (*p == 'i') {
499 
500 				/* Integral (bytes) */
501 				p++;
502 
503 				*fmt = 'i';
504 				field_size = 8;
505 				field_width = strtol(p, &intendp, 10);
506 				p = intendp;
507 				state = DONE;
508 			} else if (*p == 'b') {
509 
510 				/* Bits */
511 				p++;
512 
513 				*fmt = 'b';
514 				field_size = 1;
515 				field_width = strtol(p, &intendp, 10);
516 				p = intendp;
517 				state = DONE;
518 			} else {
519 				fprintf(stderr, "Invalid startfield %c "
520 					"(%02x)\n", *p, *p);
521 				is_error = 1;
522 				state = DONE;
523 			}
524 			break;
525 
526 		case DONE:
527 			break;
528 		}
529 	}
530 
531 	if (is_error) {
532 		*error_p = 1;
533 		return (0);
534 	}
535 
536 	*error_p = 0;
537 	*pp = p;
538 	*width_p = field_width * field_size;
539 	*value_p = value;
540 	*suppress_p = suppress;
541 
542 	return (something);
543 }
544 
545 static int
546 do_encode(u_char *buff, size_t vec_max, size_t *used,
547 	  int (*arg_get)(void *, char *), void *gethook, const char *fmt,
548 	  va_list *ap)
549 {
550 	int ind;
551 	int shift;
552 	u_char val;
553 	int ret;
554 	int width, value, error, suppress;
555 	char c;
556 	int encoded = 0;
557 	char field_name[80];
558 
559 	ind = 0;
560 	shift = 0;
561 	val = 0;
562 
563  	while ((ret = next_field(&fmt, &c, &width, &value, field_name,
564 				 sizeof(field_name), &error, &suppress))) {
565 		encoded++;
566 
567 		if (ret == 2) {
568 			if (suppress)
569 				value = 0;
570 			else
571 				value = arg_get != NULL ?
572 					(*arg_get)(gethook, field_name) :
573 					va_arg(*ap, int);
574 		}
575 
576 #if 0
577 		printf(
578 "do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d suppress %d\n",
579 		ret, c, width, value, field_name, error, suppress);
580 #endif
581 		/* Absolute seek */
582 		if (c == 's') {
583 			ind = value;
584 			continue;
585 		}
586 
587 		/* A width of < 8 is a bit field. */
588 		if (width < 8) {
589 
590 			/* This is a bit field.  We start with the high bits
591 			 * so it reads the same as the SCSI spec.
592 			 */
593 
594 			shift += width;
595 
596 			val |= (value << (8 - shift));
597 
598 			if (shift == 8) {
599 				if (ind < vec_max) {
600 					buff[ind++] = val;
601 					val = 0;
602 				}
603 				shift = 0;
604 			}
605 		} else {
606 			if (shift) {
607 				if (ind < vec_max) {
608 					buff[ind++] = val;
609 					val = 0;
610 				}
611 				shift = 0;
612 			}
613 			switch(width) {
614 			case 8:		/* 1 byte integer */
615 				if (ind < vec_max)
616 					buff[ind++] = value;
617 				break;
618 
619 			case 16:	/* 2 byte integer */
620 				if (ind < vec_max - 2 + 1) {
621 					buff[ind++] = value >> 8;
622 					buff[ind++] = value;
623 				}
624 				break;
625 
626 			case 24:	/* 3 byte integer */
627 				if (ind < vec_max - 3 + 1) {
628 					buff[ind++] = value >> 16;
629 					buff[ind++] = value >> 8;
630 					buff[ind++] = value;
631 				}
632 				break;
633 
634 			case 32:	/* 4 byte integer */
635 				if (ind < vec_max - 4 + 1) {
636 					buff[ind++] = value >> 24;
637 					buff[ind++] = value >> 16;
638 					buff[ind++] = value >> 8;
639 					buff[ind++] = value;
640 				}
641 				break;
642 
643 			default:
644 				fprintf(stderr, "do_encode: Illegal width\n");
645 				break;
646 			}
647 		}
648 	}
649 
650 	/* Flush out any remaining bits
651 	 */
652 	if (shift && ind < vec_max) {
653 		buff[ind++] = val;
654 		val = 0;
655 	}
656 
657 
658 	if (used)
659 		*used = ind;
660 
661 	if (error)
662 		return (-1);
663 
664 	return (encoded);
665 }
666 
667 int
668 csio_decode(struct ccb_scsiio *csio, const char *fmt, ...)
669 {
670 	va_list ap;
671 	int retval;
672 
673 	va_start(ap, fmt);
674 
675 	retval = do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
676 	    NULL, NULL, fmt, &ap);
677 
678 	va_end(ap);
679 
680 	return (retval);
681 }
682 
683 int
684 csio_decode_visit(struct ccb_scsiio *csio, const char *fmt,
685 		  void (*arg_put)(void *, int, void *, int, char *),
686 		  void *puthook)
687 {
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 	return (do_buff_decode(csio->data_ptr, (size_t)csio->dxfer_len,
697 		    arg_put, puthook, fmt, NULL));
698 }
699 
700 int
701 buff_decode(uint8_t *buff, size_t len, const char *fmt, ...)
702 {
703 	va_list ap;
704 	int retval;
705 
706 	va_start(ap, fmt);
707 
708 	retval = do_buff_decode(buff, len, NULL, NULL, fmt, &ap);
709 
710 	va_end(ap);
711 
712 	return (retval);
713 }
714 
715 int
716 buff_decode_visit(uint8_t *buff, size_t len, const char *fmt,
717 		  void (*arg_put)(void *, int, void *, int, char *),
718 		  void *puthook)
719 {
720 
721 	/*
722 	 * We need some way to output things; we can't do it without
723 	 * the arg_put function.
724 	 */
725 	if (arg_put == NULL)
726 		return (-1);
727 
728 	return (do_buff_decode(buff, len, arg_put, puthook, fmt, NULL));
729 }
730 
731 /*
732  * Build a SCSI CCB, given the command and data pointers and a format
733  * string describing the
734  */
735 int
736 csio_build(struct ccb_scsiio *csio, uint8_t *data_ptr, uint32_t dxfer_len,
737 	   uint32_t flags, int retry_count, int timeout, const char *cmd_spec,
738 	   ...)
739 {
740 	size_t cmdlen;
741 	int retval;
742 	va_list ap;
743 
744 	if (csio == NULL)
745 		return (0);
746 
747 	bzero(csio, sizeof(struct ccb_scsiio));
748 
749 	va_start(ap, cmd_spec);
750 
751 	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
752 				&cmdlen, NULL, NULL, cmd_spec, &ap)) == -1)
753 		goto done;
754 
755 	cam_fill_csio(csio,
756 		      /* retries */ retry_count,
757 		      /* cbfcnp */ NULL,
758 		      /* flags */ flags,
759 		      /* tag_action */ MSG_SIMPLE_Q_TAG,
760 		      /* data_ptr */ data_ptr,
761 		      /* dxfer_len */ dxfer_len,
762 		      /* sense_len */ SSD_FULL_SIZE,
763 		      /* cdb_len */ cmdlen,
764 		      /* timeout */ timeout ? timeout : 5000);
765 
766 done:
767 	va_end(ap);
768 
769 	return (retval);
770 }
771 
772 int
773 csio_build_visit(struct ccb_scsiio *csio, uint8_t *data_ptr,
774 		 uint32_t dxfer_len, uint32_t flags, int retry_count,
775 		 int timeout, const char *cmd_spec,
776 		 int (*arg_get)(void *hook, char *field_name), void *gethook)
777 {
778 	size_t cmdlen;
779 	int retval;
780 
781 	if (csio == NULL)
782 		return (0);
783 
784 	/*
785 	 * We need something to encode, but we can't get it without the
786 	 * arg_get function.
787 	 */
788 	if (arg_get == NULL)
789 		return (-1);
790 
791 	bzero(csio, sizeof(struct ccb_scsiio));
792 
793 	if ((retval = do_encode(csio->cdb_io.cdb_bytes, SCSI_MAX_CDBLEN,
794 				&cmdlen, arg_get, gethook, cmd_spec, NULL)) == -1)
795 		return (retval);
796 
797 	cam_fill_csio(csio,
798 		      /* retries */ retry_count,
799 		      /* cbfcnp */ NULL,
800 		      /* flags */ flags,
801 		      /* tag_action */ MSG_SIMPLE_Q_TAG,
802 		      /* data_ptr */ data_ptr,
803 		      /* dxfer_len */ dxfer_len,
804 		      /* sense_len */ SSD_FULL_SIZE,
805 		      /* cdb_len */ cmdlen,
806 		      /* timeout */ timeout ? timeout : 5000);
807 
808 	return (retval);
809 }
810 
811 int
812 csio_encode(struct ccb_scsiio *csio, const char *fmt, ...)
813 {
814 	va_list ap;
815 	int retval;
816 
817 	if (csio == NULL)
818 		return (0);
819 
820 	va_start(ap, fmt);
821 
822 	retval = do_encode(csio->data_ptr, csio->dxfer_len, NULL, NULL, NULL,
823 	    fmt, &ap);
824 
825 	va_end(ap);
826 
827 	return (retval);
828 }
829 
830 int
831 buff_encode_visit(uint8_t *buff, size_t len, const char *fmt,
832 		  int (*arg_get)(void *hook, char *field_name), void *gethook)
833 {
834 
835 	/*
836 	 * We need something to encode, but we can't get it without the
837 	 * arg_get function.
838 	 */
839 	if (arg_get == NULL)
840 		return (-1);
841 
842 	return (do_encode(buff, len, NULL, arg_get, gethook, fmt, NULL));
843 }
844 
845 int
846 csio_encode_visit(struct ccb_scsiio *csio, const char *fmt,
847 		  int (*arg_get)(void *hook, char *field_name), void *gethook)
848 {
849 
850 	/*
851 	 * We need something to encode, but we can't get it without the
852 	 * arg_get function.
853 	 */
854 	if (arg_get == NULL)
855 		return (-1);
856 
857 	return (do_encode(csio->data_ptr, csio->dxfer_len, NULL, arg_get,
858 			 gethook, fmt, NULL));
859 }
860