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