diff --git a/lib/libscsi/Makefile b/lib/libscsi/Makefile new file mode 100644 index 000000000000..b0b7ac1fbd7f --- /dev/null +++ b/lib/libscsi/Makefile @@ -0,0 +1,19 @@ +# @(#)Makefile 8.1 (Berkeley) 6/4/93 + +LIB= scsi +#CFLAGS+=-DLIBC_SCCS -I/sys +SRCS= scsi.c + +MAN3= scsi.3 + +#MLINKS+=kvm_getprocs.3 kvm_getargv.3 kvm_getprocs.3 kvm_getenvv.3 +#MLINKS+=kvm_open.3 kvm_openfiles.3 kvm_open.3 kvm_close.3 +#MLINKS+=kvm_read.3 kvm_write.3 + +beforeinstall: + -cd ${.CURDIR}; cmp -s scsi.h ${DESTDIR}/usr/include/scsi.h || \ + install -c -o ${BINOWN} -g ${BINGRP} -m 444 scsi.h \ + ${DESTDIR}/usr/include + + +.include diff --git a/lib/libscsi/scsi.3 b/lib/libscsi/scsi.3 new file mode 100644 index 000000000000..e6d02756af81 --- /dev/null +++ b/lib/libscsi/scsi.3 @@ -0,0 +1,344 @@ +.\" Copyright (c) 1994 HD Associates (hd@world.std.com) +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by HD Associates +.\" 4. Neither the name of the HD Associaates nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd November 20, 1994 +.Dt SCSI 3 +.Os +.Sh NAME +.Nm scsireq_buff_decode , +.Nm scsireq_build , +.Nm scsireq_decode , +.Nm scsireq_encode +.Nm scsireq_enter +.Nm scsireq_new , +.Nm scsireq_reset , +.Nm SCSIREQ_ERROR , +.Nm scsi_open +.Nm scsi_debug +.Nm scsi_debug_output +.Nd SCSI User library +.Sh SYNOPSIS +.Fd #include +.Fd #include +.Fd #include +.Ft int +.Fn scsireq_buff_decode "u_char *ptr, size_t len, char *fmt, ..." +.Ft struct scsireq * +.Fn scsireq_build "struct scsireq *s, u_long len, caddr_t buf, u_long flags, char *fmt, ..." +.Ft int +.Fn scsireq_decode "struct scsireq *, char *fmt, ..." +.Ft int +.Fn scsireq_encode "struct scsireq *, char *fmt, ..." +.Ft int +.Fn scsireq_enter "int fid, struct scsireq *s" +.Ft struct scsireq * +.Fn scsireq_new void +.Ft struct scsireq * +.Fn scsireq_reset "struct scsireq *" +.Ft int +.Fn SCSIREQ_ERROR "struct scsireq *" +.Ft int +.Fn scsi_open "const char *path, int flags" +.Ft void +.Fn scsi_debug "FILE *f, int ret, struct scsireq *s" +.Ft FILE * +.Fn scsi_debug_output "char *s" +.Sh DESCRIPTION +These functions +use the SCIOCCOMMAND +.Xr ioctl 2 +of the FreeBSD SCSI subsystem +to provide user level access to SCSI commands. +The programmer must know the SCSI CDB (Command Descriptor +Block) to perform the desired command. These functions assist in +building up the CDB, submitting it to the SCSI subsystem, and decoding +the result. +.Pp +To provide for security, +not all devices accept the SCIOCCOMAND ioctl. For tape +drives, only the control device accepts it. For disk drives, only +the RAWPART partition (partition d in 2.0) accepts it. Any device +that comes on line as an UNKNOWN device will accept the ioctl, and +the "super scsi" +.Xr ssc 4 +device also accepts it. +.Pp +Most of the SCSI library functions build up and manipulate the +.Ar scsireq +structure found in the include file +.Aq Pa sys/scsiio.h : +.Bd -literal -offset indent +#define SENSEBUFLEN 48 +.Pp +typedef struct scsireq { + u_long flags; /* info about the request status and type */ + u_long timeout; + u_char cmd[16]; /* 12 is actually the max */ + u_char cmdlen; + caddr_t databuf; /* address in user space of buffer */ + u_long datalen; /* size of user buffer (request) */ + u_long datalen_used; /* size of user buffer (used)*/ + u_char sense[SENSEBUFLEN]; /* returned sense will be in here */ + u_char senselen; /* sensedata request size (MAX of SENSEBUFLEN)*/ + u_char senselen_used; /* return value only */ + u_char status; /* what the scsi status was from the adapter */ + u_char retsts; /* the return status for the command */ + int error; /* error bits */ +} scsireq_t; +.Ed +.Pp +The function +.Fn scsireq_new +allocates a new +.Ar scsireq +structure and returns a pointer to it or NULL if it can't allocate +memory. +.Pp +.Fn scsireq_reset +resets the structure to reasonable values and returns the same pointer passed +in to it. +It gracefully handles the NULL pointer passed in to it so that you can +unconditionally use +.Ar scsireq_new . +.Pp +.Fn scsireq_build +builds up a scsireq structure based on the information provided in +the variable argument list. +It gracefully handles a NULL pointer passed to it. +.Pp +.Fr len +is the length of the data phase; the data transfer direction is +determined by the +.Ar flags +argument. +.Pp +.Fr buf +is the data buffer used during the SCSI data phase. If it is NULL it +is allocated via malloc and +.Ar scsireq->databuf +is set to point to the newly allocated memory. +.Pp +.Fr flags +are the flags defined in +.Aq Pa sys/scsiio.h : +.Bd -literal -offset indent +/* bit defintions for flags */ +#define SCCMD_READ 0x00000001 +#define SCCMD_WRITE 0x00000002 +#define SCCMD_IOV 0x00000004 +#define SCCMD_ESCAPE 0x00000010 +#define SCCMD_TARGET 0x00000020 +.Ed +Only two of these flags are supported in this release of the software: +.Fr SCCMD_READ +indicates a data in phase (a transfer into the user buffer at +.Ar scsireg->databuf +), and +.Fr SCCMD_WRITE +indicates a data out phase (a transfer out of the user buffer). +.Pp +.Fr fmt +is an ASCII CDB format specifier used to build up the SCSI CDB. +This text string is made up of a list of field specifiers. Field +specifiers specify the value for each CDB field (including indicating +that the value be taken from the next argument in the +variable argument list), the width +of the field in bits or bytes, and an optional name. +The optional name is the final part of a field specifier and +is in curly braces. A valid example is: +.Bd -literal -offset indent +.Fr "v:b1 {PS} 0:b1 {Reserved} v:b6 {Page Code}" +.Ed +This field specifier has two one bit fields and one six bit field. +The second one bit field is the constant value 0 and the first +one bit field and the six bit field are taken from the variable +argument list. +Multi byte fields are swapped into the SCSI byte order in the +CDB and +white space is ignored. +.Pp +When the field is a hex value or the letter v, (e.g., +.Fr "1A" +or +.Fr "v" ) +a single byte value +is copied to the next unused byte of the CDB. +When the letter +.Fr v +is used the next integer argument is taken from the variable argument list +and that value used. +.Pp +A constant hex value followed by a field width specifier or the letter +.Fr v +followed by a field width specifier (e.g., +.Fr 3:4 , +.Fr 3:b4 , +.Fr 3:i3 , +.FR v:i3 ) +is used to specify a field of a given bit or byte width. +Either the constant value or (for the V specifier) the next integer value from +the variable argument list is copied to the next unused +bits or bytes of the CDB. A decimal number or the letter +.Fr b +followed by a decimal +number as the field width indicates a bit field of that width. +These bit fields are packed as tightly as possible beginning with the +high bit (so that it reads the same as the SCSI spec), and a new byte of +the CDB is +started whenever the byte fills completely or when an +.Fr i +field is encountered. +.Pp +A field width specifier consisting of the letter +.Fr i +followed by either +1, 2, 3 or 4 indicates a 1, 2, 3 or 4 byte integral value that must +be swapped into SCSI byte order (MSB first). +.Pp +For the v field specifier +the next integer argument is taken from the variable argument +list and that value is used swapped into SCSI byte order. +.Pp +.Fn scsireq_decode +is used to decode information from the data in phase of the SCSI +transfer. +The decoding is similar to +the command specifier processing of +.Fn scsireq_build, +except that the data is extracted from the data pointed to by +.Fr scsireq->databuf. +The stdarg list must be pointers to integers instead of integer +values. +.Pp +In addition, a seek field type and a suppression field modifier are added. +The +.Fr * +suppression modifier to a field (e.g., +.Fr *i3 +or +.Fr *b4 ) +suppresses the assignment from that field and can be used to skip +over bytes or bits in the data, without copying them to a dummy variable +in the arg list. +.Pp +A seek field type +.Fr s +is provided. This ``seeks'' to an absolute position in the data ( +.Fr s3 ) +or a relative position ( +.Fr s+3 ) +in the data, based on whether or not the seek value has a '+' sign. +The value can also be +.Fr v + and the next integer value will be taken from the arg list +and used as the seek value. +.Pp +.Fn scsireq_buff_decode +decodes an arbitrary data buffer identically to the method +used by the +.Fn scsireq_decode +function. +.Pp +.Fn scsireq_encode +encodes the data phase section of the structure. The encoding is +handled identically as the encoding of the CDB structure by +.Fn scsireq_build +.Pp +.Fn scsireq_enter +submits the built up structure for processing using +the SCIOCCOMMAND ioctl. +.Pp +.Fn SCSIREQ_ERROR +is a macro that determines if the result of the SCIOCCOMMAND ioctl may +have been +in error by examining the host adapter return code, whether sense was sent +or not, and so on. +.Pp +.Fn scsi_open +checks environment variables and initializes the library for +consistent library use and then calls the regular open system call. +.Pp +.Fn scsi_debug and +.Fn scsi_debug_output +are used for debugging. +.Pp +.Fn scsi_debug +prints the results of a scsireq_enter function to the specified stdio +stream. +.Pp +.Fn scsi_debug_output +requests that the results of all transactions be debugged to the +supplied file using +.Fn scsi_debug . +.Sh RETURN VALUES +The function +.Fn scsireq_new +returns a pointer to storage allocated from malloc, and therefore +potentially a NULL. +.Pp +The functions +.Fn scsireq_build +and +.Fn scsireq_reset +return the same pointer as the one passed in. +.Pp +The functions +.Fn scsireq_buff_decode , +.Fn scsireq_decode +and +.Fn scsireq_encode +return the number of fields processed. +.Pp +The function +.Fn scsireq_enter +returns the result of the ioctl call. +.Sh SEE ALSO +.Xr scsi 8 , +.Xr scsi 4 +.Sh BUGS +This only works completely for the 1542C. The host adapter code +that sets up the residual amount of data transfer has to be added +to each individual adapter. This library is usable on the other +host adapters, however, the SCSI driver pretends that the proper +amount of data is always transferred. If you have an Adaptec 174x +and can hack contact dufault@hda.com and you can have the code to +calculate residual data for the 174x series to integrate and test. +.Sh HISTORY +Many systems have comparable interfaces to permit a user to construct a +SCSI command in user space. +.Pp +The data structure is almost identical to the SGI /dev/scsi data +structure. If anyone knows the name of the authors it should +go here; Peter Dufault first read about it in a 1989 Sun Expert magazine. +.Pp +Peter Dufault implemented a clone of SGI's interface in 386bsd that +led to this library and the related kernel ioctl. +If anyone needs that for compatability contact dufault@hda.com. diff --git a/lib/libscsi/scsi.c b/lib/libscsi/scsi.c new file mode 100644 index 000000000000..d5869aa7882a --- /dev/null +++ b/lib/libscsi/scsi.c @@ -0,0 +1,1124 @@ +/* scsi: SCSI user library + */ +/* Copyright (c) 1994 HD Associates + * (contact: dufault@hda.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by HD Associates + * 4. Neither the name of the HD Associaates nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * $Id:$ + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "scsi.h" + +static struct +{ + FILE *db_f; + int db_level; + int db_trunc; +} behave; + +/* scsireq_reset: Reset a scsireq structure. + */ +scsireq_t *scsireq_reset(scsireq_t *scsireq) +{ + if (scsireq == 0) + return scsireq; + + scsireq->flags = 0; /* info about the request status and type */ + scsireq->timeout = 2000; /* 2 seconds */ + bzero(scsireq->cmd, sizeof(scsireq->cmd)); + scsireq->cmdlen = 0; + /* Leave scsireq->databuf alone */ + /* Leave scsireq->datalen alone */ + scsireq->datalen_used = 0; + bzero(scsireq->sense, sizeof(scsireq->sense)); + scsireq->senselen = sizeof(scsireq->sense); + scsireq->senselen_used = 0; + scsireq->status = 0; + scsireq->retsts = 0; + scsireq->error = 0; + + return scsireq; +} + +/* scsireq_new: Allocate and initialize a new scsireq. + */ +scsireq_t *scsireq_new(void) +{ + scsireq_t *p = (scsireq_t *)malloc(sizeof(scsireq_t)); + + if (p) + scsireq_reset(p); + + return p; +} + +/* + * Decode: Decode the data section of a scsireq. This decodes + * trivial grammar: + * + * fields : field fields + * ; + * + * field : field_specifier + * | control + * ; + * + * control : 's' seek_value + * | 's' '+' seek_value + * ; + * + * seek_value : DECIMAL_NUMBER + * | 'v' // For indirect seek, i.e., value from the arg list + * ; + * + * field_specifier : type_specifier field_width + * | '{' NAME '}' type_specifier field_width + * ; + * + * field_width : DECIMAL_NUMBER + * ; + * + * type_specifier : 'i' // Integral types (i1, i2, i3, i4) + * | 'b' // Bits + * | 'c' // Character arrays + * | 'z' // Character arrays with zeroed trailing spaces + * ; + * + * Notes: + * 1. Integral types are swapped into host order. + * 2. Bit fields are allocated MSB to LSB to match the SCSI spec documentation. + * 3. 's' permits "seeking" in the string. "s+DECIMAL" seeks relative to + * DECIMAL; "sDECIMAL" seeks absolute to decimal. + * 4. 's' permits an indirect reference. "sv" or "s+v" will get the + * next integer value from the arg array. + * 5. Field names can be anything between the braces + * + * BUGS: + * i and b types are promoted to ints. + * + */ + +#define ARG_PUT(ARG) \ +do \ +{ \ + if (arg_put) \ + (*arg_put)(puthook, letter, (void *)((long)(ARG)), 1, field_name); \ + else \ + *(va_arg(ap, int *)) = (ARG); \ +} while (0) + +static int do_buff_decode(u_char *databuf, size_t len, +void (*arg_put)(void *, int , void *, int, char *), void *puthook, +char *fmt, va_list ap) +{ + int decoded = 0; + int width; + int suppress; + int plus; + int done = 0; + static u_char mask[] = {0, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + int value; + u_char *base = databuf; + char letter; + char field_name[80]; + + u_char bits = 0; /* For bit fields */ + int shift = 0; /* Bits already shifted out */ + suppress = 0; + + while (!done) + { + switch(letter = *fmt) + { + case ' ': + case '\t': + fmt++; + break; + + case '*': /* Suppress assignment */ + fmt++; + suppress = 1; + break; + + case '{': /* Field Name */ + { + int i = 0; + fmt++; /* Skip '{' */ + while (*fmt && (*fmt != '}')) + { + if (i < sizeof(field_name)) + field_name[i++] = *fmt; + + fmt++; + } + fmt++; /* Skip '}' */ + field_name[i] = 0; + } + break; + + case 'b': /* Bits */ + fmt++; + width = strtol(fmt, &fmt, 10); + if (width > 8) + done = 1; + else + { + if (shift <= 0) + { + bits = *databuf++; + shift = 8; + } + value = (bits >> (shift - width)) & mask[width]; + +#if 0 + printf("shift %2d bits %02x value %02x width %2d mask %02x\n", + shift, bits, value, width, mask[width]); +#endif + + if (!suppress) + ARG_PUT(value); + else + suppress = 0; + + shift -= width; + } + + break; + + case 'i': /* Integral values */ + shift = 0; + fmt++; + width = strtol(fmt, &fmt, 10); + switch(width) + { + case 1: + if (!suppress) + ARG_PUT(*databuf); + else + suppress = 0; + databuf++; + break; + + case 2: + if (!suppress) + ARG_PUT( + (*databuf) << 8 | + *(databuf + 1)); + else + suppress = 0; + databuf += 2; + break; + + case 3: + if (!suppress) + ARG_PUT( + (*databuf) << 16 | + (*(databuf + 1)) << 8 | + *(databuf + 2)); + else + suppress = 0; + databuf += 3; + break; + + case 4: + if (!suppress) + ARG_PUT( + (*databuf) << 24 | + (*(databuf + 1)) << 16 | + (*(databuf + 2)) << 8 | + *(databuf + 3)); + else + suppress = 0; + databuf += 4; + break; + + default: + done = 1; + } + + break; + + case 'c': /* Characters (i.e., not swapped) */ + case 'z': /* Characters with zeroed trailing spaces */ + shift = 0; + fmt++; + width = strtol(fmt, &fmt, 10); + if (!suppress) + { + + if (arg_put) + (*arg_put)(puthook, letter, databuf, width, field_name); + else + { + char *dest; + dest = va_arg(ap, char *); + bcopy(databuf, dest, width); + if (letter == 'z') + { + char *p; + for (p = dest + width - 1; + (p >= (char *)dest) && (*p == ' '); p--) + *p = 0; + } + } + } + else + suppress = 0; + databuf += width; + decoded++; + break; + + case 's': /* Seek */ + shift = 0; + fmt++; + if (*fmt == '+') + { + plus = 1; + fmt++; + } + else + plus = 0; + + if (tolower(*fmt) == 'v') + { + /* You can't suppress a seek value. You also + * can't have a variable seek when you are using + * "arg_put". + */ + width = (arg_put) ? 0 : va_arg(ap, int); + fmt++; + } + else + width = strtol(fmt, &fmt, 10); + + if (plus) + databuf += width; /* Relative seek */ + else + databuf = base + width; /* Absolute seek */ + + decoded++; + break; + + case 0: + done = 1; + break; + + default: + fprintf(stderr, "Unknown letter in format: %c\n", letter); + fmt++; + } + } + + return decoded; +} + +int scsireq_decode(scsireq_t *scsireq, char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + return do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen, + 0, 0, fmt, ap); +} + +int scsireq_decode_visit(scsireq_t *scsireq, char *fmt, +void (*arg_put)(void *, int , void *, int, char *), void *puthook) +{ + va_list ap = (va_list)0; + va_start (ap, fmt); + return do_buff_decode(scsireq->databuf, (size_t)scsireq->datalen, + arg_put, puthook, fmt, ap); +} + +int scsireq_buff_decode(u_char *buff, size_t len, char *fmt, ...) +{ + va_list ap; + va_start (ap, fmt); + return do_buff_decode(buff, len, 0, 0, fmt, ap); +} + +/* next_field: Return the next field in a command specifier. This + * builds up a SCSI command using this trivial grammar: + * + * fields : field fields + * ; + * + * field : value + * | value ':' field_width + * ; + * + * field_width : digit + * | 'i' digit // i2 = 2 byte integer, i3 = 3 byte integer etc. + * ; + * + * value : HEX_NUMBER + * | 'v' // For indirection. + * ; + * + * Notes: + * Bit fields are specified MSB first to match the SCSI spec. + * + * Examples: + * TUR: "0 0 0 0 0 0" + * WRITE BUFFER: "38 v:3 0:2 0:3 v v:i3 v:i3 0", mode, buffer_id, list_length + * + * The function returns the value: + * 0: For reached end, with error_p set if an error was found + * 1: For valid stuff setup + * 2: For "v" was entered as the value (implies use varargs) + * + */ + +static int next_field(char **pp, +char *fmt, int *width_p, int *value_p, char *name, int n_name, int *error_p) +{ + char *p = *pp; + + int something = 0; + + enum + { + BETWEEN_FIELDS, + START_FIELD, + GET_FIELD, + DONE, + } state; + + int value = 0; + int field_size; /* Default to byte field type... */ + int field_width; /* 1 byte wide */ + int is_error = 0; + + field_size = 8; /* Default to byte field type... */ + *fmt = 'i'; + field_width = 1; /* 1 byte wide */ + if (name) + *name = 0; + + state = BETWEEN_FIELDS; + + while (state != DONE) + { + switch(state) + { + case BETWEEN_FIELDS: + if (*p == 0) + state = DONE; + else if (isspace(*p)) + p++; + else if (*p == '{') + { + int i = 0; + + p++; + + while (*p && *p != '}') + { + if(name && i < n_name) + { + name[i] = *p; + i++; + } + p++; + } + + if(name && i < n_name) + name[i] = 0; + + if (*p == '}') + p++; + } + else if (isxdigit(*p)) + { + something = 1; + value = strtol(p, &p, 16); + state = START_FIELD; + } + else if (tolower(*p) == 'v') + { + p++; + something = 2; + value = *value_p; + state = START_FIELD; + } + else if (tolower(*p) == 's') /* Seek */ + { + *fmt = 's'; + p++; + if (tolower(*p) == 'v') + { + p++; + something = 2; + value = *value_p; + } + else + { + something = 1; + value = strtol(p, &p, 0); + } + state = DONE; + } + else + { + fprintf(stderr, "Invalid starting character\n"); + is_error = 1; + state = DONE; + } + break; + + case START_FIELD: + if (*p == ':') + { + p++; + field_size = 1; /* Default to bits when specified */ + state = GET_FIELD; + } + else + state = DONE; + break; + + case GET_FIELD: + if (isdigit(*p)) + { + *fmt = 'b'; + field_size = 1; + field_width = strtol(p, &p, 10); + state = DONE; + } + else if (*p == 'i') /* Integral (bytes) */ + { + p++; + + *fmt = 'i'; + field_size = 8; + field_width = strtol(p, &p, 10); + state = DONE; + } + else if (*p == 'b') /* Bits */ + { + p++; + + *fmt = 'b'; + field_size = 1; + field_width = strtol(p, &p, 10); + state = DONE; + } + else + { + fprintf(stderr, "Invalid startfield %c (%02x)\n", + *p, *p); + is_error = 1; + state = DONE; + } + break; + + case DONE: + break; + } + } + + if (is_error) + { + *error_p = 1; + return 0; + } + + *error_p = 0; + *pp = p; + *width_p = field_width * field_size; + *value_p = value; + + return something; +} + +static int do_encode(u_char *buff, size_t vec_max, size_t *used, +int (*arg_get)(void *, char *), void *gethook, +char *fmt, va_list ap) +{ + int ind; + int shift; + u_char val; + int ret; + int width, value, error; + char c; + int encoded = 0; + char field_name[80]; + + ind = 0; + shift = 0; + val = 0; + + while ((ret = next_field(&fmt, + &c, &width, &value, field_name, sizeof(field_name), &error))) + { + encoded++; + + if (ret == 2) + value = arg_get ? (*arg_get)(gethook, field_name) : va_arg(ap, int); + +#if 0 + printf("do_encode: ret %d fmt %c width %d value %d name \"%s\" error %d\n", + ret, c, width, value, field_name, error); +#endif + + if (c == 's') /* Absolute seek */ + { + ind = value; + continue; + } + + if (width < 8) /* A width of < 8 is a bit field. */ + { + + /* This is a bit field. We start with the high bits + * so it reads the same as the SCSI spec. + */ + + shift += width; + + val |= (value << (8 - shift)); + + if (shift == 8) + { + if (ind < vec_max) + { + buff[ind++] = val; + val = 0; + } + shift = 0; + } + } + else + { + if (shift) + { + if (ind < vec_max) + { + buff[ind++] = val; + val = 0; + } + shift = 0; + } + switch(width) + { + case 8: /* 1 byte integer */ + if (ind < vec_max) + buff[ind++] = value; + break; + + case 16: /* 2 byte integer */ + if (ind < vec_max - 2 + 1) + { + buff[ind++] = value >> 8; + buff[ind++] = value; + } + break; + + case 24: /* 3 byte integer */ + if (ind < vec_max - 3 + 1) + { + buff[ind++] = value >> 16; + buff[ind++] = value >> 8; + buff[ind++] = value; + } + break; + + case 32: /* 4 byte integer */ + if (ind < vec_max - 4 + 1) + { + buff[ind++] = value >> 24; + buff[ind++] = value >> 16; + buff[ind++] = value >> 8; + buff[ind++] = value; + } + break; + + default: + fprintf(stderr, "do_encode: Illegal width\n"); + break; + } + } + } + + /* Flush out any remaining bits + */ + if (shift && ind < vec_max) + { + buff[ind++] = val; + val = 0; + } + + + if (used) + *used = ind; + + if (error) + return -1; + + return encoded; +} + +/* XXX: Should be a constant in scsiio.h + */ +#define CMD_BUFLEN 16 + +scsireq_t *scsireq_build(scsireq_t *scsireq, + u_long datalen, caddr_t databuf, u_long flags, + char *cmd_spec, ...) +{ + int cmdlen; + va_list ap; + + if (scsireq == 0) + return 0; + + scsireq_reset(scsireq); + + if (databuf) + { + scsireq->databuf = databuf; + scsireq->datalen = datalen; + scsireq->flags = flags; + } + else if (datalen) + { + /* XXX: Good way to get a memory leak. Perhaps this should be + * removed. + */ + if ( (scsireq->databuf = malloc(datalen)) == 0) + return 0; + + scsireq->datalen = datalen; + scsireq->flags = flags; + } + + va_start(ap, cmd_spec); + + if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, 0, 0, cmd_spec, ap) == -1) + return 0; + + scsireq->cmdlen = cmdlen; + return scsireq; +} + +scsireq_t +*scsireq_build_visit(scsireq_t *scsireq, + u_long datalen, caddr_t databuf, u_long flags, char *cmd_spec, + int (*arg_get)(void *hook, char *field_name), void *gethook) +{ + int cmdlen; + va_list ap = (va_list) 0; + + if (scsireq == 0) + return 0; + + scsireq_reset(scsireq); + + if (databuf) + { + scsireq->databuf = databuf; + scsireq->datalen = datalen; + scsireq->flags = flags; + } + else if (datalen) + { + /* XXX: Good way to get a memory leak. Perhaps this should be + * removed. + */ + if ( (scsireq->databuf = malloc(datalen)) == 0) + return 0; + + scsireq->datalen = datalen; + scsireq->flags = flags; + } + + if (do_encode(scsireq->cmd, CMD_BUFLEN, &cmdlen, arg_get, gethook, + cmd_spec, ap) == -1) + return 0; + + scsireq->cmdlen = cmdlen; + + return scsireq; +} + +int scsireq_encode(scsireq_t *scsireq, char *fmt, ...) +{ + va_list ap; + + if (scsireq == 0) + return 0; + + va_start(ap, fmt); + + return do_encode(scsireq->databuf, + scsireq->datalen, 0, 0, 0, fmt, ap); +} + +int scsireq_encode_visit(scsireq_t *scsireq, char *fmt, + int (*arg_get)(void *hook, char *field_name), void *gethook) +{ + va_list ap = (va_list)0; + return do_encode(scsireq->databuf, scsireq->datalen, 0, + arg_get, gethook, fmt, ap); +} + +FILE *scsi_debug_output(char *s) +{ + if (s == 0) + behave.db_f = 0; + else + { + behave.db_f = fopen(s, "w"); + + if (behave.db_f == 0) + behave.db_f = stderr; + } + + return behave.db_f; +} + +#define SCSI_TRUNCATE -1 + +typedef struct scsi_assoc +{ + int code; + char *text; +} scsi_assoc_t; + +static scsi_assoc_t retsts[] = +{ + { SCCMD_OK, "No error" }, + { SCCMD_TIMEOUT, "Command Timeout" }, + { SCCMD_BUSY, "Busy" }, + { SCCMD_SENSE, "Sense Returned" }, + { SCCMD_UNKNOWN, "Unknown return status" }, + + { 0, 0 } +}; + +static char *scsi_assoc_text(int code, scsi_assoc_t *tab) +{ + while (tab->text) + { + if (tab->code == code) + return tab->text; + + tab++; + } + + return "Unknown code"; +} + +void scsi_dump(FILE *f, char *text, u_char *p, int req, int got, int dump_print) +{ + int i; + int trunc = 0; + + if (f == 0 || req == 0) + return; + + fprintf(f, "%s (%d of %d):\n", text, got, req); + + if (behave.db_level == 0 && (behave.db_trunc != -1 && got > behave.db_trunc)) + { + trunc = 1; + got = behave.db_trunc; + } + + for (i = 0; i < got; i++) + { + fprintf(f, "%02x", p[i]); + + putc(' ', f); + + if ((i % 16) == 15 || i == got - 1) + { + int j; + if (dump_print) + { + fprintf(f, " # "); + for (j = i - 15; j <= i; j++) + putc((isprint(p[j]) ? p[j] : '.'), f); + + putc('\n', f); + } + else + putc('\n', f); + } + } + + fprintf(f, "%s", (trunc) ? "(truncated)...\n" : "\n"); +} + +/* XXX: sense_7x_dump and scsi_sense dump was just sort of + * grabbed out of the old ds + * library and not really merged in carefully. It should use the + * new buffer decoding stuff. + */ + +/* Get unsigned long. + */ +static u_long g_u_long(u_char *s) +{ + return (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3]; +} + +/* In the old software you could patch in a special error table: + */ +scsi_assoc_t *error_table = 0; + +static void sense_7x_dump(FILE *f, scsireq_t *scsireq) +{ + int code; + u_char *s = (u_char *)scsireq->sense; + int valid = (*s) & 0x80; + u_long val; + + static scsi_assoc_t sense[] = { + { 0, "No sense" }, + { 1, "Recovered error" }, + { 2, "Not Ready" }, + { 3, "Medium error" }, + { 4, "Hardware error" }, + { 5, "Illegal request" }, + { 6, "Unit attention" }, + { 7, "Data protect" }, + { 8, "Blank check" }, + { 9, "Vendor specific" }, + { 0xa, "Copy aborted" }, + { 0xb, "Aborted Command" }, + { 0xc, "Equal" }, + { 0xd, "Volume overflow" }, + { 0xe, "Miscompare" }, + { 0, 0 }, + }; + + static scsi_assoc_t code_tab[] = { + {0x70, "current errors"}, + {0x71, "deferred errors"}, + }; + + fprintf(f, "Error code is \"%s\"\n", scsi_assoc_text(s[0]&0x7F, code_tab)); + fprintf(f, "Segment number is %02x\n", s[1]); + + if (s[2] & 0x20) + fprintf(f, "Incorrect Length Indicator is set.\n"); + + fprintf(f, "Sense key is \"%s\"\n", scsi_assoc_text(s[2] & 0x7, sense)); + + val = g_u_long(s + 3); + fprintf(f, "The Information field is%s %08lx (%ld).\n", + valid ? "" : " not valid but contains", (long)val, (long)val); + + val = g_u_long(s + 8); + fprintf(f, "The Command Specific Information field is %08lx (%ld).\n", + (long)val, (long)val); + + fprintf(f, "Additional sense code: %02x\n", s[12]); + fprintf(f, "Additional sense code qualifier: %02x\n", s[13]); + + code = (s[12] << 8) | s[13]; + + if (error_table) + fprintf(f, "%s\n", scsi_assoc_text(code, error_table)); + + if (s[15] & 0x80) + { + if ((s[2] & 0x7) == 0x05) /* Illegal request */ + { + int byte; + u_char value, bit; + fprintf(f, "Illegal parameter in the %s.\n", + (s[15] & 0x40) ? "parameter list" : "command descriptor block"); + byte = ((s[16] << 8) | s[17]); + value = (u_char)scsireq->cmd[byte]; + bit = s[15] & 0x7; + if (s[15] & 0x08) + fprintf(f, "Bit %d of byte %d (value %02x) is illegal.\n", + bit, byte, value); + else + fprintf(f, "Byte %d (value %02x) is illegal.\n", byte, value); + } + else + { + fprintf(f, "Sense Key Specific (valid but not illegal request):\n"); + fprintf(f, + "%02x %02x %02x\n", s[15] & 0x7f, s[16], s[17]); + } + } +} + +/* scsi_sense_dump: Dump the sense portion of the scsireq structure. + */ +void scsi_sense_dump(FILE *f, scsireq_t *scsireq) +{ + u_char *s = (u_char *)scsireq->sense; + int code = (*s) & 0x7f; + + if (scsireq->senselen_used == 0) + { + fprintf(f, "No sense sent.\n"); + return; + } + +#if 0 + if (!valid) + fprintf(f, "The sense data is not valid.\n"); +#endif + + switch(code) + { + case 0x70: + case 0x71: + sense_7x_dump(f, scsireq); + break; + + default: + fprintf(f, "No sense dump for error code %02x.\n", code); + scsi_dump(f, "sense", s, scsireq->senselen, scsireq->senselen_used, 0); + } +} + +void scsi_retsts_dump(FILE *f, scsireq_t *scsireq) +{ + if (scsireq->retsts == 0) + return; + + fprintf(f, "return status %d (%s)", + scsireq->retsts, scsi_assoc_text(scsireq->retsts, retsts)); + + switch(scsireq->retsts) + { + case SCCMD_TIMEOUT: + fprintf(f, " after %ld ms", scsireq->timeout); + break; + + default: + break; + } +} + +int scsi_debug(FILE *f, int ret, scsireq_t *scsireq) +{ + char *d; + if (f == 0) + return 0; + + fprintf(f, "SCIOCCOMMAND ioctl"); + + if (ret == 0) + fprintf(f, ": Command accepted."); + else + { + if (ret != -1) + fprintf(f, ", return value %d?", ret); + + if (errno) + { + fprintf(f, ": %s", strerror(errno)); + errno = 0; + } + } + + fputc('\n', f); + + if (ret == 0 && (scsireq->status || scsireq->retsts)) + { + scsi_retsts_dump(f, scsireq); + + if (scsireq->status) + fprintf(f, " host adapter status %d\n", scsireq->status); + + if (scsireq->flags & SCCMD_READ) + d = "Data in"; + else if (scsireq->flags & SCCMD_WRITE) + d = "Data out"; + else + d = "No data transfer?"; + + if (scsireq->cmdlen == 0) + fprintf(f, "Zero length command????\n"); + + scsi_dump(f, "Command out", + (u_char *)scsireq->cmd, scsireq->cmdlen, scsireq->cmdlen, 0); + scsi_dump(f, d, + (u_char *)scsireq->databuf, scsireq->datalen, + scsireq->datalen_used, 1); + scsi_sense_dump(f, scsireq); + } + + fflush(f); + + return ret; +} + +static char *debug_output; + +int scsi_open(const char *path, int flags) +{ + int fd = open(path, flags); + + if (fd != -1) + { + char *p; + debug_output = getenv("SU_DEBUG_OUTPUT"); + (void)scsi_debug_output(debug_output); + + if ((p = getenv("SU_DEBUG_LEVEL"))) + sscanf(p, "%d", &behave.db_level); + + if ((p = getenv("SU_DEBUG_TRUNCATE"))) + sscanf(p, "%d", &behave.db_trunc); + else + behave.db_trunc = SCSI_TRUNCATE; + } + + return fd; +} + +int scsireq_enter(int fid, scsireq_t *scsireq) +{ + int ret; + + if (scsireq == 0) + return EFAULT; + + ret = ioctl(fid, SCIOCCOMMAND, (void *)scsireq); + + if (behave.db_f) scsi_debug(behave.db_f, ret, scsireq); + + return ret; +} diff --git a/lib/libscsi/scsi.h b/lib/libscsi/scsi.h new file mode 100644 index 000000000000..c9aa0f3e23d1 --- /dev/null +++ b/lib/libscsi/scsi.h @@ -0,0 +1,80 @@ +/* Copyright (c) 1994 HD Associates (hd@world.std.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by HD Associates + * 4. Neither the name of the HD Associaates nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _SCSI_H_ +#define _SCSI_H_ + +#include + +/* libscsi: Library header file for the SCSI user library. + */ + +#include +#include + +#define SCSIREQ_ERROR(SR) (\ +0 \ +|| SR->senselen_used /* Sent sense */ \ +|| SR->status /* Host adapter status */ \ +|| SR->retsts /* SCSI transfer status */ \ +|| SR->error /* copy of errno */ \ +) + +scsireq_t *scsireq_reset __P((scsireq_t *)); +scsireq_t *scsireq_new __P((void)); + +int scsireq_buff_decode __P((u_char *, size_t, char *, ...)); + +int scsireq_decode __P((scsireq_t *, char *, ...)); +int scsireq_decode_visit __P((scsireq_t *, char *, +void (*) (void *, int, void *, int, char *), void *)); + +int scsireq_encode __P((scsireq_t *, char *, ...)); +int scsireq_encode_visit __P((scsireq_t *, char *, +int (*)(void *, char *), void *)); + +scsireq_t *scsireq_build __P((scsireq_t *, + u_long, caddr_t, u_long, + char *, ...)); + +scsireq_t *scsireq_build_visit __P((scsireq_t *, + u_long, caddr_t, u_long, char *, + int (*)(void *, char *), void *)); + +int scsireq_enter __P((int, scsireq_t *)); + +void scsi_dump __P((FILE *, char *, u_char *, int, int, int )); + +int scsi_debug __P((FILE *, int, scsireq_t *)); +FILE *scsi_debug_output __P((char *)); +int scsi_open __P((const char *, int )); + +#endif /* _SCSI_H_ */