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