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