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