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