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: [email protected])
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
do_buff_decode(u_int8_t * buff,size_t len,void (* arg_put)(void *,int,void *,int,char *),void * puthook,const char * fmt,va_list * ap)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
next_field(const char ** pp,char * fmt,int * width_p,int * value_p,char * name,int n_name,int * error_p,int * suppress_p)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
do_encode(u_char * buff,size_t vec_max,size_t * used,int (* arg_get)(void *,char *),void * gethook,const char * fmt,va_list * ap)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
csio_decode(struct ccb_scsiio * csio,const char * fmt,...)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
csio_decode_visit(struct ccb_scsiio * csio,const char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)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
buff_decode(u_int8_t * buff,size_t len,const char * fmt,...)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
buff_decode_visit(u_int8_t * buff,size_t len,const char * fmt,void (* arg_put)(void *,int,void *,int,char *),void * puthook)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
csio_build(struct ccb_scsiio * csio,u_int8_t * data_ptr,u_int32_t dxfer_len,u_int32_t flags,int retry_count,int timeout,const char * cmd_spec,...)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
csio_build_visit(struct ccb_scsiio * csio,u_int8_t * data_ptr,u_int32_t dxfer_len,u_int32_t flags,int retry_count,int timeout,const char * cmd_spec,int (* arg_get)(void * hook,char * field_name),void * gethook)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
csio_encode(struct ccb_scsiio * csio,const char * fmt,...)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
buff_encode_visit(u_int8_t * buff,size_t len,const char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)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
csio_encode_visit(struct ccb_scsiio * csio,const char * fmt,int (* arg_get)(void * hook,char * field_name),void * gethook)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