Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libcam/scsi_cmdparse.c
39475 views
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/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(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
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
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
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
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
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
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
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
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
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
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
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
861