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