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 */
36
37#include <sys/cdefs.h>
38__FBSDID("$FreeBSD: stable/10/lib/libcam/scsi_cmdparse.c 315122 2017-03-12 04:53:27Z ngie $");
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
102static int
103do_buff_decode(u_int8_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
357static int
358next_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
545static int
546do_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
667int
668csio_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
683int
684csio_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
700int
701buff_decode(u_int8_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
715int
716buff_decode_visit(u_int8_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 */
735int
736csio_build(struct ccb_scsiio *csio, u_int8_t *data_ptr, u_int32_t dxfer_len,
737	   u_int32_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
766done:
767	va_end(ap);
768
769	return (retval);
770}
771
772int
773csio_build_visit(struct ccb_scsiio *csio, u_int8_t *data_ptr,
774		 u_int32_t dxfer_len, u_int32_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
811int
812csio_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
830int
831buff_encode_visit(u_int8_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
845int
846csio_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