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