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