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