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