diff options
-rw-r--r-- | include/Makefile | 4 | ||||
-rw-r--r-- | lib/libutil/Makefile | 22 | ||||
-rw-r--r-- | lib/libutil/scsi.3 | 350 | ||||
-rw-r--r-- | lib/libutil/scsi.c | 1193 | ||||
-rw-r--r-- | lib/libutil/scsi.h | 86 | ||||
-rw-r--r-- | sbin/Makefile | 4 | ||||
-rw-r--r-- | sbin/scsi/Makefile | 8 | ||||
-rw-r--r-- | sbin/scsi/scsi.8 | 246 | ||||
-rw-r--r-- | sbin/scsi/scsi.c | 995 | ||||
-rw-r--r-- | share/misc/Makefile | 4 | ||||
-rw-r--r-- | share/misc/scsi_modes | 233 |
11 files changed, 3136 insertions, 9 deletions
diff --git a/include/Makefile b/include/Makefile index 3e67cc267a4..02321084192 100644 --- a/include/Makefile +++ b/include/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.14 1996/06/11 23:45:06 deraadt Exp $ +# $OpenBSD: Makefile,v 1.15 1996/06/12 11:21:39 deraadt Exp $ # $NetBSD: Makefile,v 1.59 1996/05/15 21:36:43 jtc Exp $ # @(#)Makefile 5.45.1.1 (Berkeley) 5/6/91 @@ -30,7 +30,7 @@ LDIRS= dev net netinet netccitt netiso netns nfs sys ufs vm ddb RDIRS= ../lib/libcurses ../lib/libform ../lib/libmenu ../lib/libocurses \ ../lib/libpanel ../lib/librpcsvc ../lib/libtermlib ../lib/libedit \ - ../lib/libpcap ../sys/arch/${MACHINE} + ../lib/libpcap ../lib/libutil ../sys/arch/${MACHINE} NOOBJ= noobj diff --git a/lib/libutil/Makefile b/lib/libutil/Makefile index 0e629329d6a..c5c39110312 100644 --- a/lib/libutil/Makefile +++ b/lib/libutil/Makefile @@ -1,13 +1,13 @@ -# $OpenBSD: Makefile,v 1.2 1996/05/22 11:34:58 deraadt Exp $ +# $OpenBSD: Makefile,v 1.3 1996/06/12 11:21:32 deraadt Exp $ # $NetBSD: Makefile,v 1.8 1996/05/16 07:03:28 thorpej Exp $ LIB= util CFLAGS+=-DLIBC_SCCS SRCS= getmaxpartitions.c getrawpartition.c login.c login_tty.c logout.c \ - logwtmp.c passwd.c pty.c + logwtmp.c passwd.c pty.c scsi.c MAN= getmaxpartitions.3 getrawpartition.3 login.3 openpty.3 pw_init.3 \ - pw_lock.3 + pw_lock.3 scsi.3 MLINKS+=login.3 logout.3 MLINKS+=login.3 logwtmp.3 @@ -20,5 +20,21 @@ MLINKS+=pw_init.3 pw_scan.3 MLINKS+=pw_init.3 pw_error.3 MLINKS+=pw_lock.3 pw_mkdb.3 MLINKS+=pw_lock.3 pw_abort.3 +MLINKS+=scsi.3 scsireq_buff_decode.3 +MLINKS+=scsi.3 scsireq_build.3 +MLINKS+=scsi.3 scsireq_decode.3 +MLINKS+=scsi.3 scsireq_encode.3 +MLINKS+=scsi.3 scsireq_enter.3 +MLINKS+=scsi.3 scsireq_new.3 +MLINKS+=scsi.3 scsireq_reset.3 +MLINKS+=scsi.3 SCSIREQ_ERROR.3 +MLINKS+=scsi.3 scsi_open.3 +MLINKS+=scsi.3 scsi_debug.3 +MLINKS+=scsi.3 scsi_debug_output.3 + +includes: + -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 <bsd.lib.mk> diff --git a/lib/libutil/scsi.3 b/lib/libutil/scsi.3 new file mode 100644 index 00000000000..4b39ad62f19 --- /dev/null +++ b/lib/libutil/scsi.3 @@ -0,0 +1,350 @@ +.\" $OpenBSD: scsi.3,v 1.1 1996/06/12 11:21:34 deraadt Exp $ +.\" 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 Associates 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 <sys/types.h> +.Fd #include <sys/scsiio.h> +.Fd #include <scsi.h> +.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 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 +Look at the +.Xr scsi 8 +command before using the library directly - simple programs are +best implemented as scripts using that facility. +.Pp +To provide for security, +not all devices accept the SCIOCCOMAND ioctl. It is accepted by the +control device for tape drives, partition D for disk drives, partition C +for CD ROM drives, and any "unknown" device. +The "super scsi" +.Xr ssc 4 +device also accepts the ioctl. +.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 +.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 definitions 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 a 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. White space is +ignored, and the pound sign ('#') introduces a comment that ends at the +end of the current line. +.Pp +The optional name is the first part of a field specifier and +is in curly braces. The text in curly braces in this example are +the names: +.Bd -literal -offset indent +.Fr "{PS} v:b1 {Reserved} 0:b1 {Page Code} v:b6 # Mode select page" +.Ed +.Pp +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" ) +then 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 ) +specifies 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. +.Pp +A decimal number or the letter +.Fr b +followed by a decimal number field width indicates a bit field of that width. +The 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 a 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 +.Fr 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. +.Pp +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 should be pointers to integers instead of integer +values. +A seek field type and a suppression modifier are added. +The +.Fr * +suppression modifier (e.g., +.Fr *i3 +or +.Fr *b4 ) +suppresses assignment from the field and can be used to skip +over bytes or bits in the data, without having to copy +them to a dummy variable in the arg list. +.Pp +The seek field type +.Fr s +permits you to skip over data. +This seeks to an absolute position ( +.Fr s3 ) +or a relative position ( +.Fr s+3 ) +in the data, based on whether or not the presence of the '+' sign. +The seek value can be specified as +.Fr v +and the next integer value from the argument list will be +used as the seek value. +.Pp +.Fn scsireq_buff_decode +decodes an arbitrary data buffer using the method +described above in +.Fn scsireq_decode . +.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 +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 and +.Fn scsireq_decode +return the number of assignments performed. +.Pp +.Fn scsireq_encode +returns 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 compatibility contact dufault@hda.com. diff --git a/lib/libutil/scsi.c b/lib/libutil/scsi.c new file mode 100644 index 00000000000..1733a908657 --- /dev/null +++ b/lib/libutil/scsi.c @@ -0,0 +1,1193 @@ +/* $OpenBSD: scsi.c,v 1.1 1996/06/12 11:21:35 deraadt Exp $ */ + +/* 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. + * + * $FreeBSD: scsi.c,v 1.6 1995/05/30 05:47:26 rgrimes Exp $ + */ +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <sys/scsiio.h> +#include <sys/errno.h> +#include <stdarg.h> +#include <fcntl.h> + +#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 + * | 't' // 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. + * + */ + +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 assigned = 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]; + +# define ARG_PUT(ARG) \ + do \ + { \ + if (!suppress) \ + { \ + if (arg_put) \ + (*arg_put)(puthook, (letter == 't' ? 'b' : letter), \ + (void *)((long)(ARG)), 1, field_name); \ + else \ + *(va_arg(ap, int *)) = (ARG); \ + assigned++; \ + } \ + field_name[0] = 0; \ + suppress = 0; \ + } while (0) + + u_char bits = 0; /* For bit fields */ + int shift = 0; /* Bits already shifted out */ + suppress = 0; + field_name[0] = 0; + + while (!done) + { + switch(letter = *fmt) + { + case ' ': /* White space */ + case '\t': + case '\r': + case '\n': + case '\f': + fmt++; + break; + + case '#': /* Comment */ + while (*fmt && (*fmt != '\n')) + fmt++; + if (fmt) + fmt++; /* Skip '\n' */ + 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++; + } + if (fmt) + fmt++; /* Skip '}' */ + field_name[i] = 0; + } + break; + + case 't': /* Bit (field) */ + 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 + + ARG_PUT(value); + + shift -= width; + } + + break; + + case 'i': /* Integral values */ + shift = 0; + fmt++; + width = strtol(fmt, &fmt, 10); + switch(width) + { + case 1: + ARG_PUT(*databuf); + databuf++; + break; + + case 2: + ARG_PUT( + (*databuf) << 8 | + *(databuf + 1)); + databuf += 2; + break; + + case 3: + ARG_PUT( + (*databuf) << 16 | + (*(databuf + 1)) << 8 | + *(databuf + 2)); + databuf += 3; + break; + + case 4: + ARG_PUT( + (*databuf) << 24 | + (*(databuf + 1)) << 16 | + (*(databuf + 2)) << 8 | + *(databuf + 3)); + 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 == 't' ? 'b' : 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; + } + } + assigned++; + } + databuf += width; + field_name[0] = 0; + suppress = 0; + 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 */ + + break; + + case 0: + done = 1; + break; + + default: + fprintf(stderr, "Unknown letter in format: %c\n", letter); + fmt++; + } + } + + return assigned; +} + +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; + 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); +} + +int scsireq_buff_decode_visit(u_char *buff, size_t len, char *fmt, +void (*arg_put)(void *, int, void *, int, char *), void *puthook) +{ + va_list ap = (va_list)0; + return do_buff_decode(buff, len, arg_put, puthook, 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, +int *suppress_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; + int suppress = 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 == '#') + { + while (*p && *p != '\n') + p++; + if (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 (*p == '*') + { + p++; + suppress = 1; + } + 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; + } +/* Try to work without the "v". + */ + else if (tolower(*p) == 'i') + { + something = 2; + value = *value_p; + p++; + + *fmt = 'i'; + field_size = 8; + field_width = strtol(p, &p, 10); + state = DONE; + } + +/* XXX: B can't work: Sees the 'b' as a hex digit in "isxdigit". + * try "t" for bit field. + */ + else if (tolower(*p) == 't') + { + something = 2; + value = *value_p; + p++; + + *fmt = 'b'; + field_size = 1; + field_width = strtol(p, &p, 10); + state = DONE; + } + 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: %c\n", *p); + 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; + *suppress_p = suppress; + + 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, suppress; + 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, &suppress))) + { + encoded++; + + if (ret == 2) { + if (suppress) + value = 0; + else + 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 suppress %d\n", + ret, c, width, value, field_name, error, suppress); +#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_buff_encode_visit(u_char *buff, size_t len, char *fmt, + int (*arg_get)(void *hook, char *field_name), void *gethook) +{ + va_list ap = (va_list)0; + return do_encode(buff, len, 0, + arg_get, gethook, 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_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; + int bad_par = ((s[15] & 0x40) == 0); + fprintf(f, "Illegal value in the %s.\n", + (bad_par ? "parameter list" : "command descriptor block")); + byte = ((s[16] << 8) | s[17]); + value = bad_par ? (u_char)scsireq->databuf[byte] : (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 || behave.db_level)) + { + 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/libutil/scsi.h b/lib/libutil/scsi.h new file mode 100644 index 00000000000..46ac8cbf73d --- /dev/null +++ b/lib/libutil/scsi.h @@ -0,0 +1,86 @@ +/* $OpenBSD: scsi.h,v 1.1 1996/06/12 11:21:37 deraadt Exp $ */ + +/* 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 <sys/cdefs.h> + +/* libscsi: Library header file for the SCSI user library. + */ + +#include <sys/scsiio.h> +#include <stdio.h> + +#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_buff_decode_visit __P((u_char *, size_t, char *, +void (*a)(void *, int, void *, int, char *), void *)); + +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 *)); +int scsireq_buff_encode_visit __P((u_char *, size_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_ */ diff --git a/sbin/Makefile b/sbin/Makefile index d97399f0c4d..e4d47573728 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.13 1996/06/11 10:37:46 deraadt Exp $ +# $OpenBSD: Makefile,v 1.14 1996/06/12 11:21:23 deraadt Exp $ # $NetBSD: Makefile,v 1.29 1996/05/14 17:39:21 ws Exp $ # Not ported: XNSrouted enpload scsiformat startslip @@ -6,7 +6,7 @@ SUBDIR= badsect disklabel ccdconfig dmesg fastboot ifconfig init ipf ipfstat \ ipnat kbd mknod modload modunload mount mountd nfsd nfsiod \ - nologin ping quotacheck reboot route routed savecore shutdown \ + nologin ping quotacheck reboot route routed savecore scsi shutdown \ slattach swapon ttyflags umount # support for various file systems diff --git a/sbin/scsi/Makefile b/sbin/scsi/Makefile new file mode 100644 index 00000000000..755249c0b79 --- /dev/null +++ b/sbin/scsi/Makefile @@ -0,0 +1,8 @@ +# $OpenBSD: Makefile,v 1.1 1996/06/12 11:21:41 deraadt Exp $ + +PROG= scsi +MAN= scsi.8 +LDADD= -lutil +DPADD= ${LIBUTIL} + +.include <bsd.prog.mk> diff --git a/sbin/scsi/scsi.8 b/sbin/scsi/scsi.8 new file mode 100644 index 00000000000..79fc38714ee --- /dev/null +++ b/sbin/scsi/scsi.8 @@ -0,0 +1,246 @@ +.\" $OpenBSD: scsi.8,v 1.1 1996/06/12 11:21:42 deraadt Exp $ +.\" $FreeBSD: scsi.8,v 1.5 1995/05/05 20:41:58 dufault Exp $ +.\" +.\" Written By Julian ELischer +.\" Copyright julian Elischer 1993. +.\" Permission is granted to use or redistribute this file in any way as long +.\" as this notice remains. Julian Elischer does not guarantee that this file +.\" is totally correct for any given task and users of this file must +.\" accept responsibility for any damage that occurs from the application of this +.\" file. +.\" +.\" (julian@tfs.com julian@dialix.oz.au) +.\" User SCSI hooks added by Peter Dufault: +.\" +.\" 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. The name of HD Associates +.\" may not 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 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 October 11, 1993 +.Dt SCSI 8 +.Os BSD 4 +.Sh NAME +.Nm scsi +.Nd program to assist with scsi devices. +.Sh SYNOPSIS +.Bd -literal -offset +Usage: +scsi -f device -d debug_level # To set debug level +scsi -f device [-v] -z seconds # To freeze bus +scsi -f device -m page [-P pc] [-e] # To read mode pages +scsi -f device -p [-b bus] [-l lun] # To probe all devices +scsi -f device -r [-b bus] [-t targ] [-l lun] # To reprobe a device +scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command... + -o count out_fmt [arg0 ... argn] # EITHER (for data out) + -i count in_fmt # OR (for data in) +.Pp +"out_fmt" can be "-" to read output data from stdin; +"in_fmt" can be "-" to write input data to stdout; +.Pp +If debugging is not compiled in the kernel, "-d" will have no effect +.Ed +.Sh DESCRIPTION +The +.Nm scsi +program is used to send commands to a scsi device. It is also +a sample usage of the user level SCSI commands. +.Pp +The +.Fr -d +option sets the SCSI kernel debug level. The kernel must have been compiled +with the +.Em SCSIDEBUG +option. See +.Fr /sys/scsi/scsi_debug.h +to figure out what to set the kernel debug level to. +.Pp +The +.Fr -z +option freezes all activity on all SCSI busses for a given number of +seconds. If +.Fr -v +is also specified then a BEL character is sent to the standard +output at the start and finish of the bus freeze. +This requires that the kernel be built with the SCSI_FREEZE kernel option. +This kernel code is not committed yet. +.Pp +The +.Fr -m +option is used to read a device mode page. The file +.Fr /usr/share/misc/scsi_modes +is read to look at for how to interpret the mode data. The environment +variable SCSI_MODES can specify a different file to use. +.Pp +.in +.25i +The +.Fr -P +option can be used to specify a page control field. The page control +fields are +.Bd -literal -offset +0 Current Values +1 Changeable Values +2 Default Values +3 Saved Values +.Ed +.Pp +The +.Fr -e +option permits you to edit the fields. It will use the editor specified +by your EDITOR environment variable. To store changes permanently, +edit page control 3 using the +.Fr -P +flag. +.in -.25i +.Pp +The +.Fr -p +option can be used against the "super scsi" device +.Fr /dev/scsi/super +to probe all devices with a given SCSI lun on a given SCSI bus. +The bus can be selected with the -b option and the default is 0. +The lun can be selected with the -l option and the default is 0. +See +.Xr scsi 4 +for a description of the "super scsi" device. +.Pp +The +.Fr -r +option can be used in FreeBSD 1.1 to reprobe a specific SCSI device at a given +Bus, Target and Lun. +This is not needed in FreeBSD 2.1, since opening a fixed SCSI device +has the side effect of reprobing it, and probing with the bus with the +-p option should bring on line any newly found devices. +See +.Xr scsi 4 +for a description of fixed scsi devices. +.Pp +The +.Fr -c +option permits you to send user level SCSI commands specified on +the command line to a +device. The command is sent using the SCIOCCOMMAND ioctl, so the +device you are accessing must permit this ioctl. See +.Xr scsi 4 +for full details of which minor devices permit the ioctl, and +.Xr scsi 3 +for the full details on how to build up the commands and data phases +using the format arguments. +.in +.25i +.Pp +.Fr -v +turns on more verbose information. +.Pp +.Fr -s +sets the command timeout in seconds. The default is two seconds. +.Pp +.Fr "-c cmd_fmt" +specifies the command as described in +.Xr scsi 3 "." +The additional arguments provide values for any variables +specified in the command format. +.Pp +.Fr "-o count out_fmt arg0 ... argn" +indicates that this is a data out command (i.e., data will be sent from +the system to the device) with +.Fr count +bytes of data. The data out is built up using the facilities described in +.Xr scsi 3 +using the provided arguments to fill in any integer variables. +.Fr out_fmt +can be specified as a hyphen ("-") to indicate that the +.Fr count +bytes of data should be read from the standard input. +.Pp +.Fr "-i count in_fmt" +indicates that this is a data in command (i.e., data will be read from +the device into the system) with +.Fr count +bytes of data read in. The information is extracted according to +.Fr in_fmt +using the facilities described in +.Xr scsi 3 +and displayed on the standard output. +.Fr in_fmt +can be specified as a hyphen ("-") to indicate that the +.Fr count +bytes of data input should be written to the standard output. +.in -.25i +.Sh EXAMPLES +To verify that the device type for the disk /dev/rsd0c is 0 +(direct access device): +.Bd -literal -offset +root# scsi -f /dev/rsd0c -c "12 0 0 0 64 0" -i 64 "*b3 b5" +0 +.Ed +.Pp +To do an inquiry to /dev/rsd2c: +.Bd -literal -offset +root# scsi -f /dev/rsd2c -c "12 0 0 0 64 0" -i 64 "s8 z8 z16 z4" +FUJITSU M2654S-512 010P +.Pp +To edit mode page 1 on /dev/rsd2c, and store it permanently on the +drive: +.Bd -literal -offset +root# scsi -f /dev/rsd2c -m 1 -e -P 3 +.Ed +.Pp +.Sh ENVIRONMENT +The SU_DEBUG_OUTPUT variable can be set to a file to send debugging +output to that file. +.Pp +The SU_DEBUG_LEVEL variable can be set to a non-zero integer to increase +the level of debugging. Currently this is a on or off thing; it should +perhaps use the ioctl to set the debug level in the kernel and then set +it back to zero at program exit. +.Pp +The SU_DEBUG_TRUNCATE variable can be set to an integer to limit the +amount of data phase output sent to the debugging file. +.Pp +The EDITOR variable determines the editor to use for the mode editor. +.Sh SEE ALSO +.Xr scsi 4 , +.Xr scsi 3 +.Sh BUGS +.Pp +This command wasn't ready for inclusion in 2.0R and so is missing in +that release. +.Pp +Some devices respond to an inquiry for all LUNS. This will cause them +to come on line to 8 times during reprobe to different logical units. +.Pp +The "-i" option to do an inquiry went away in 2.1. The new facilities +provided by "-c" supercede that. +.Pp +Check your permissions carefully. +"scsi -f /dev/rsd0c -c "4 0 0 0 0 0" permits anyone who can open +/dev/rsd0c to format the disk drive. This must be changed to +at least require write access to the drive. +.Sh HISTORY +The +.Nm scsi +command appeared in 386BSD 0.1.2.4/FreeBSD to support the new reprobe +and user SCSI commands. diff --git a/sbin/scsi/scsi.c b/sbin/scsi/scsi.c new file mode 100644 index 00000000000..8915446f6ab --- /dev/null +++ b/sbin/scsi/scsi.c @@ -0,0 +1,995 @@ +/* $OpenBSD: scsi.c,v 1.1 1996/06/12 11:21:43 deraadt Exp $ */ +/* $FreeBSD: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $ */ + +/* + * Written By Julian ELischer + * Copyright julian Elischer 1993. + * Permission is granted to use or redistribute this file in any way as long + * as this notice remains. Julian Elischer does not guarantee that this file + * is totally correct for any given task and users of this file must + * accept responsibility for any damage that occurs from the application of this + * file. + * + * (julian@tfs.com julian@dialix.oz.au) + * + * User SCSI hooks added by Peter Dufault: + * + * 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. The name of HD Associates + * may not 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 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. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/scsiio.h> +#include <sys/file.h> +#include <scsi.h> +#include <ctype.h> +#include <signal.h> + +int fd; +int debuglevel; +int debugflag; +int commandflag; +int reprobe; +#ifdef SCIOCADDR +int probe_all; +#endif +int verbose = 0; +int bus = -1; /* all busses */ +int targ = -1; /* all targs */ +int lun = 0; /* just lun 0 */ +#ifdef SCIOCFREEZE +int freeze = 0; /* Freeze this many seconds */ +#endif + +int modeflag; +int editflag; +int modepage = 0; /* Read this mode page */ +int pagectl = 0; /* Mode sense page control */ +int seconds = 2; + +void usage(void) +{ + printf( + +"Usage:\n" +"\n" +" scsi -f device -d debug_level # To set debug level\n" +#ifdef SCIOCFREEZE +" scsi -f device [-v] -z seconds # To freeze bus\n" +#endif +" scsi -f device -m page [-P pc] # To read mode pages\n" +" scsi -f device -p [-b bus] [-l lun] # To probe all devices\n" +" scsi -f device -r [-b bus] [-t targ] [-l lun] # To reprobe a device\n" +" scsi -f device [-v] [-s seconds] -c cmd_fmt [arg0 ... argn] # A command...\n" +" -o count out_fmt [arg0 ... argn] # EITHER (data out)\n" +" -i count in_fmt # OR (data in)\n" +"\n" +"\"out_fmt\" can be \"-\" to read output data from stdin;\n" +"\"in_fmt\" can be \"-\" to write input data to stdout;\n" +"\n" +"If debugging is not compiled in the kernel, \"-d\" will have no effect\n" + +); + + exit (1); +} + +void procargs(int *argc_p, char ***argv_p) +{ + int argc = *argc_p; + char **argv = *argv_p; + extern char *optarg; + extern int optind; + int fflag, + ch; + + fflag = 0; + commandflag = 0; + debugflag = 0; + while ((ch = getopt(argc, argv, "ceprvf:d:b:t:l:z:m:P:s:")) != EOF) { + switch (ch) { +#ifdef SCIOCADDR + case 'p': + probe_all = 1; + break; +#endif + case 'r': + reprobe = 1; + break; + case 'c': + commandflag = 1; + break; + case 'v': + verbose = 1; + break; + case 'e': + editflag = 1; + break; + case 'f': + if ((fd = scsi_open(optarg, O_RDWR)) < 0) { + (void) fprintf(stderr, + "%s: unable to open device %s: %s\n", + argv[0], optarg, strerror(errno)); + exit(errno); + } + fflag = 1; + break; + case 'd': + debuglevel = strtol(optarg, 0, 0); + debugflag = 1; + break; + case 'b': + bus = strtol(optarg, 0, 0); + break; + case 't': + targ = strtol(optarg, 0, 0); + break; + case 'l': + lun = strtol(optarg, 0, 0); + break; +#ifdef SCIOCFREEZE + case 'z': + freeze = strtol(optarg, 0, 0); + break; +#endif + case 'P': + pagectl = strtol(optarg, 0, 0); + break; + case 's': + seconds = strtol(optarg, 0, 0); + break; + case 'm': + modeflag = 1; + modepage = strtol(optarg, 0, 0); + break; + case '?': + default: + usage(); + } + } + *argc_p = argc - optind; + *argv_p = argv + optind; + + if (!fflag) usage(); +} + +/* get_hook: Structure for evaluating args in a callback. + */ +struct get_hook +{ + int argc; + char **argv; + int got; +}; + +/* iget: Integer argument callback + */ +int iget(void *hook, char *name) +{ + struct get_hook *h = (struct get_hook *)hook; + int arg; + + if (h->got >= h->argc) + { + fprintf(stderr, "Expecting an integer argument.\n"); + usage(); + } + arg = strtol(h->argv[h->got], 0, 0); + h->got++; + + if (verbose && name && *name) + printf("%s: %d\n", name, arg); + + return arg; +} + +/* cget: char * argument callback + */ +char *cget(void *hook, char *name) +{ + struct get_hook *h = (struct get_hook *)hook; + char *arg; + + if (h->got >= h->argc) + { + fprintf(stderr, "Expecting a character pointer argument.\n"); + usage(); + } + arg = h->argv[h->got]; + h->got++; + + if (verbose && name) + printf("cget: %s: %s", name, arg); + + return arg; +} + +/* arg_put: "put argument" callback + */ +void arg_put(void *hook, int letter, void *arg, int count, char *name) +{ + if (verbose && name && *name) + printf("%s: ", name); + + switch(letter) + { + case 'i': + case 'b': + printf("%d ", (int)arg); + break; + + case 'c': + case 'z': + { + char *p = malloc(count + 1); + p[count] = 0; + strncpy(p, (char *)arg, count); + if (letter == 'z') + { + int i; + for (i = count - 1; i >= 0; i--) + if (p[i] == ' ') + p[i] = 0; + else + break; + } + printf("%s ", p); + } + + break; + + default: + printf("Unknown format letter: '%c'\n", letter); + } + if (verbose) + putchar('\n'); +} + +int arg_get (void *hook, char *field_name) +{ + printf("get \"%s\".\n", field_name); + return 0; +} + +/* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer. + */ +enum data_phase {none = 0, in, out}; + +/* do_cmd: Send a command to a SCSI device + */ +static void +do_cmd(int fd, char *fmt, int argc, char **argv) +{ + struct get_hook h; + scsireq_t *scsireq = scsireq_new(); + enum data_phase data_phase; + int count, amount; + char *data_fmt, *bp; + + h.argc = argc; + h.argv = argv; + h.got = 0; + + scsireq_reset(scsireq); + + scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h); + + /* Three choices here: + * 1. We've used up all the args and have no data phase. + * 2. We have input data ("-i") + * 3. We have output data ("-o") + */ + + if (h.got >= h.argc) + { + data_phase = none; + count = scsireq->datalen = 0; + } + else + { + char *flag = cget(&h, 0); + + if (strcmp(flag, "-o") == 0) + { + data_phase = out; + scsireq->flags = SCCMD_WRITE; + } + else if (strcmp(flag, "-i") == 0) + { + data_phase = in; + scsireq->flags = SCCMD_READ; + } + else + { + fprintf(stderr, + "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag); + usage(); + } + + count = scsireq->datalen = iget(&h, 0); + if (count) + { + data_fmt = cget(&h, 0); + + scsireq->databuf = malloc(count); + + if (data_phase == out) + { + if (strcmp(data_fmt, "-") == 0) /* Read data from stdin */ + { + bp = (char *)scsireq->databuf; + while (count > 0 && (amount = read(0, bp, count)) > 0) + { + count -= amount; + bp += amount; + } + if (amount == -1) + { + perror("read"); + exit(errno); + } + else if (amount == 0) + { + /* early EOF */ + fprintf(stderr, + "Warning: only read %lu bytes out of %lu.\n", + scsireq->datalen - (u_long)count, + scsireq->datalen); + scsireq->datalen -= (u_long)count; + } + } + else + { + bzero(scsireq->databuf, count); + scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h); + } + } + } + } + + + scsireq->timeout = seconds * 1000; + + if (scsireq_enter(fd, scsireq) == -1) + { + scsi_debug(stderr, -1, scsireq); + exit(errno); + } + + if (SCSIREQ_ERROR(scsireq)) + scsi_debug(stderr, 0, scsireq); + + if (count && data_phase == in) + { + if (strcmp(data_fmt, "-") == 0) /* stdout */ + { + bp = (char *)scsireq->databuf; + while (count > 0 && (amount = write(1, bp, count)) > 0) + { + count -= amount; + bp += amount; + } + if (amount < 0) + { + perror("write"); + exit(errno); + } + else if (amount == 0) + fprintf(stderr, "Warning: wrote only %d bytes out of %d.\n", + scsireq->datalen - count, + scsireq->datalen); + + } + else + { + scsireq_decode_visit(scsireq, data_fmt, arg_put, 0); + putchar('\n'); + } + } +} + +#ifdef SCIOCFREEZE +static void +freeze_ioctl(int fd, int op, void *data) +{ + if (ioctl(fd, SCIOCFREEZE, 0) == -1) { + if (errno == ENODEV) { + fprintf(stderr, + "Your kernel must be configured with option SCSI_FREEZE.\n"); + } + else + perror("SCIOCFREEZE"); + exit(errno); + } +} + +/* do_freeze: Freeze the bus for a given number of seconds. + */ +static void do_freeze(int seconds) +{ + if (seconds == -1) { + printf("Hit return to thaw: "); + fflush(stdout); + sync(); + + freeze_ioctl(fd, SCIOCFREEZE, 0); + + (void)getchar(); + + freeze_ioctl(fd, SCIOCTHAW, 0); + } + else { + sync(); + freeze_ioctl(fd, SCIOCFREEZETHAW, &seconds); + if (verbose) { + putchar('\007'); + fflush(stdout); + } + + freeze_ioctl(fd, SCIOCWAITTHAW, 0); + if (verbose) { + putchar('\007'); + fflush(stdout); + } + } +} +#endif + +void mode_sense(int fd, u_char *data, int len, int pc, int page) +{ + scsireq_t *scsireq; + + bzero(data, len); + + scsireq = scsireq_new(); + + if (scsireq_enter(fd, scsireq_build(scsireq, + len, data, SCCMD_READ, + "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0", + pc, page, len)) == -1) /* Mode sense */ + { + scsi_debug(stderr, -1, scsireq); + exit(errno); + } + + if (SCSIREQ_ERROR(scsireq)) + { + scsi_debug(stderr, 0, scsireq); + exit(-1); + } + + free(scsireq); +} + +void mode_select(int fd, u_char *data, int len, int perm) +{ + scsireq_t *scsireq; + + scsireq = scsireq_new(); + + if (scsireq_enter(fd, scsireq_build(scsireq, + len, data, SCCMD_WRITE, + "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1) /* Mode select */ + { + scsi_debug(stderr, -1, scsireq); + exit(errno); + } + + if (SCSIREQ_ERROR(scsireq)) + { + scsi_debug(stderr, 0, scsireq); + exit(-1); + } + + free(scsireq); +} + + +#define START_ENTRY '{' +#define END_ENTRY '}' + +static void +skipwhite(FILE *f) +{ + int c; + +skip_again: + + while (isspace(c = getc(f))) + ; + + if (c == '#') { + while ((c = getc(f)) != '\n' && c != EOF) + ; + goto skip_again; + } + + ungetc(c, f); +} + +/* mode_lookup: Lookup a format description for a given page. + */ +char *mode_db = "/usr/share/misc/scsi_modes"; +static char *mode_lookup(int page) +{ + char *new_db; + FILE *modes; + int match, next, found, c; + static char fmt[1024]; /* XXX This should be with strealloc */ + int page_desc; + new_db = getenv("SCSI_MODES"); + + if (new_db) + mode_db = new_db; + + modes = fopen(mode_db, "r"); + if (modes == 0) + return 0; + + next = 0; + found = 0; + + while (!found) { + + skipwhite(modes); + + if (fscanf(modes, "%i", &page_desc) != 1) + break; + + if (page_desc == page) + found = 1; + + skipwhite(modes); + if (getc(modes) != START_ENTRY) { + fprintf(stderr, "Expected %c.\n", START_ENTRY); + exit(-1); + } + + match = 1; + while (match != 0) { + c = getc(modes); + if (c == EOF) { + fprintf(stderr, "Expected %c.\n", END_ENTRY); + } + + if (c == START_ENTRY) { + match++; + } + if (c == END_ENTRY) { + match--; + if (match == 0) + break; + } + if (found && c != '\n') { + if (next >= sizeof(fmt)) { + fprintf(stderr, "Stupid program: Buffer overflow.\n"); + exit(ENOMEM); + } + + fmt[next++] = (u_char)c; + } + } + } + fmt[next] = 0; + + return (found) ? fmt : 0; +} + +/* -------- edit: Mode Select Editor --------- + */ +struct editinfo +{ + int can_edit; + int default_value; +} editinfo[64]; /* XXX Bogus fixed size */ + +static int editind; +volatile int edit_opened; +static FILE *edit_file; +static char edit_name[L_tmpnam]; + +static inline void +edit_rewind(void) +{ + editind = 0; +} + +static void +edit_done(void) +{ + int opened; + + sigset_t all, prev; + sigfillset(&all); + + (void)sigprocmask(SIG_SETMASK, &all, &prev); + + opened = (int)edit_opened; + edit_opened = 0; + + (void)sigprocmask(SIG_SETMASK, &prev, 0); + + if (opened) + { + if (fclose(edit_file)) + perror(edit_name); + if (unlink(edit_name)) + perror(edit_name); + } +} + +static void +edit_init(void) +{ + edit_rewind(); + if (tmpnam(edit_name) == 0) { + perror("tmpnam failed"); + exit(errno); + } + if ( (edit_file = fopen(edit_name, "w")) == 0) { + perror(edit_name); + exit(errno); + } + edit_opened = 1; + + atexit(edit_done); +} + +static void +edit_check(void *hook, int letter, void *arg, int count, char *name) +{ + if (letter != 'i' && letter != 'b') { + fprintf(stderr, "Can't edit format %c.\n", letter); + exit(-1); + } + + if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) { + fprintf(stderr, "edit table overflow\n"); + exit(ENOMEM); + } + editinfo[editind].can_edit = ((int)arg != 0); + editind++; +} + +static void +edit_defaults(void *hook, int letter, void *arg, int count, char *name) +{ + if (letter != 'i' && letter != 'b') { + fprintf(stderr, "Can't edit format %c.\n", letter); + exit(-1); + } + + editinfo[editind].default_value = ((int)arg); + editind++; +} + +static void +edit_report(void *hook, int letter, void *arg, int count, char *name) +{ + if (editinfo[editind].can_edit) { + if (letter != 'i' && letter != 'b') { + fprintf(stderr, "Can't report format %c.\n", letter); + exit(-1); + } + + fprintf(edit_file, "%s: %d\n", name, (int)arg); + } + + editind++; +} + +static int +edit_get(void *hook, char *name) +{ + struct get_hook *h = (struct get_hook *)hook; + int arg = editinfo[editind].default_value; + + if (editinfo[editind].can_edit) { + char line[80]; + if (fgets(line, sizeof(line), edit_file) == 0) { + perror("fgets"); + exit(errno); + } + + line[strlen(line) - 1] = 0; + + if (strncmp(name, line, strlen(name)) != 0) { + fprintf(stderr, "Expected \"%s\" and read \"%s\"\n", + name, line); + exit(-1); + } + + arg = strtoul(line + strlen(name) + 2, 0, 0); + } + + editind++; + return arg; +} + +static void +edit_edit(void) +{ + char *system_line; + char *editor = getenv("EDITOR"); + if (!editor) + editor = "vi"; + + fclose(edit_file); + + system_line = malloc(strlen(editor) + strlen(edit_name) + 6); + sprintf(system_line, "%s %s", editor, edit_name); + system(system_line); + free(system_line); + + if ( (edit_file = fopen(edit_name, "r")) == 0) { + perror(edit_name); + exit(errno); + } +} + +static void +mode_edit(int fd, int page, int edit, int argc, char *argv[]) +{ + int i; + u_char data[255]; + u_char *mode_pars; + struct mode_header + { + u_char mdl; /* Mode data length */ + u_char medium_type; + u_char dev_spec_par; + u_char bdl; /* Block descriptor length */ + }; + + struct mode_page_header + { + u_char page_code; + u_char page_length; + }; + + struct mode_header *mh; + struct mode_page_header *mph; + + char *fmt = mode_lookup(page); + if (!fmt && verbose) { + fprintf(stderr, + "No mode data base entry in \"%s\" for page %d; binary %s only.\n", + mode_db, page, (edit ? "edit" : "display")); + } + + if (edit) { + if (!fmt) { + fprintf(stderr, "Sorry: can't edit without a format.\n"); + exit(-1); + } + + if (pagectl != 0 && pagectl != 3) { + fprintf(stderr, +"It only makes sense to edit page 0 (current) or page 3 (saved values)\n"); + exit(-1); + } + + verbose = 1; + + mode_sense(fd, data, sizeof(data), 1, page); + + mh = (struct mode_header *)data; + mph = (struct mode_page_header *) + (((char *)mh) + sizeof(*mh) + mh->bdl); + + mode_pars = (char *)mph + sizeof(*mph); + + edit_init(); + scsireq_buff_decode_visit(mode_pars, mh->mdl, + fmt, edit_check, 0); + + mode_sense(fd, data, sizeof(data), 0, page); + + edit_rewind(); + scsireq_buff_decode_visit(mode_pars, mh->mdl, + fmt, edit_defaults, 0); + + edit_rewind(); + scsireq_buff_decode_visit(mode_pars, mh->mdl, + fmt, edit_report, 0); + + edit_edit(); + + edit_rewind(); + scsireq_buff_encode_visit(mode_pars, mh->mdl, + fmt, edit_get, 0); + + /* Eliminate block descriptors: + */ + bcopy((char *)mph, ((char *)mh) + sizeof(*mh), + sizeof(*mph) + mph->page_length); + + mh->bdl = 0; + mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh)); + mode_pars = ((char *)mph) + 2; + +#if 0 + /* Turn this on to see what you're sending to the + * device: + */ + edit_rewind(); + scsireq_buff_decode_visit(mode_pars, + mh->mdl, fmt, arg_put, 0); +#endif + + edit_done(); + + /* Make it permanent if pageselect is three. + */ + + mph->page_code &= ~0xC0; /* Clear PS and RESERVED */ + mh->mdl = 0; /* Reserved for mode select */ + + mode_select(fd, (char *)mh, + sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length, + (pagectl == 3)); + + exit(0); + } + + mode_sense(fd, data, sizeof(data), pagectl, page); + + /* Skip over the block descriptors. + */ + mh = (struct mode_header *)data; + mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl); + mode_pars = (char *)mph + sizeof(*mph); + + if (!fmt) { + for (i = 0; i < mh->mdl; i++) { + printf("%02x%c",mode_pars[i], + (((i + 1) % 8) == 0) ? '\n' : ' '); + } + putc('\n', stdout); + } else { + verbose = 1; + scsireq_buff_decode_visit(mode_pars, + mh->mdl, fmt, arg_put, 0); + } +} + +#ifdef SCIOCADDR +/* do_probe_all: Loop over all SCSI IDs and see if something is + * there. This only does BUS 0 LUN 0. + */ +void do_probe_all(void) +{ + scsireq_t *scsireq; + + char vendor_id[8 + 1], product_id[16 + 1], revision[4 + 1]; + int id; + u_char *inq_buf = malloc(96); + struct scsi_addr addr; + + scsireq = scsireq_build(scsireq_new(), + 96, inq_buf, SCCMD_READ, + "12 0 0 0 v 0", 96); + + addr.scbus = (bus == -1) ? 0 : bus; + addr.lun = lun; + + if (addr.scbus || addr.lun) + { + printf("For bus %d lun %d:\n", addr.scbus, addr.lun); + } + + for (id = 0; id < 8; id++) + { + addr.target = id; + + printf("%d: ", id); + if (ioctl(fd, SCIOCADDR, &addr) == -1) { + if (errno == ENXIO) + { + errno = 0; + printf("nothing.\n"); + } + else + printf("SCIOCADDR: %s\n", strerror(errno)); + + continue; + } + + if (scsireq_enter(fd, scsireq) == -1) { + printf("scsireq_enter: %s\n", strerror(errno)); + continue; + } + + vendor_id[sizeof(vendor_id) - 1] = 0; + product_id[sizeof(product_id) - 1] = 0; + revision[sizeof(revision) - 1] = 0; + + scsireq_decode(scsireq, "s8 c8 c16 c4", + vendor_id, product_id, revision); + + printf("%s %s %s\n", vendor_id, product_id, revision); + } +} +#endif + +void main(int argc, char **argv) +{ + struct scsi_addr scaddr; + + procargs(&argc,&argv); + + /* XXX This has grown to the point that it should be cleaned up. + */ +#ifdef SCIOCFREEZE + if (freeze) { + do_freeze(freeze); + } else +#endif +#ifdef SCIOCADDR + if (probe_all) { + do_probe_all(); + } else +#endif + if(reprobe) { + scaddr.scbus = bus; + scaddr.target = targ; + scaddr.lun = lun; + + if (ioctl(fd,SCIOCREPROBE,&scaddr) == -1) + perror("ioctl"); + } else if(debugflag) { + if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1) + { + perror("ioctl [SCIODEBUG]"); + exit(1); + } + } else if (commandflag) { + int i; + char *fmt; + + if (argc < 1) { + fprintf(stderr, "Need the command format string.\n"); + usage(); + } + + + fmt = argv[0]; + + argc -= 1; + argv += 1; + + do_cmd(fd, fmt, argc, argv); + } else if (modeflag) { + mode_edit(fd, modepage, editflag, argc, argv); + } + exit(0); +} diff --git a/share/misc/Makefile b/share/misc/Makefile index 06abf7fc1f2..f22cd47a0e7 100644 --- a/share/misc/Makefile +++ b/share/misc/Makefile @@ -1,8 +1,8 @@ # from: @(#)Makefile 5.13 (Berkeley) 5/7/91 -# $Id: Makefile,v 1.1.1.1 1995/10/18 08:44:43 deraadt Exp $ +# $Id: Makefile,v 1.2 1996/06/12 11:21:45 deraadt Exp $ FILES= airport ascii birthtoken eqnchar getopt inter.phone man.template \ - mdoc.template na.phone operator style zipcodes + mdoc.template na.phone operator scsi_modes style zipcodes NOOBJ= noobj all clean cleandir depend lint tags: diff --git a/share/misc/scsi_modes b/share/misc/scsi_modes new file mode 100644 index 00000000000..12838a364d8 --- /dev/null +++ b/share/misc/scsi_modes @@ -0,0 +1,233 @@ +# $OpenBSD: scsi_modes,v 1.1 1996/06/12 11:21:47 deraadt Exp $ + +# SCSI mode page data base. + +# Copyright (c) 1995 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. The name of HD Associates +# may not 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 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. + +# The ordering is alphabetical by page name, as it appears in the SCSI spec. + +# ALL DEVICE TYPES + +# Control mode page: +0x0a { + {Reserved} *t7 + {RLEC} t1 + {Queue Algorithm Modifier} t4 + {Reserved} *t2 + {QErr} t1 + {DQue} t1 + {EECA} t1 + {Reserved} *t4 + {RAENP} t1 + {UAAENP} t1 + {EAENP} t1 + {Reserved} *i1 + {Ready AEN Holdoff Period} i2 +} + +# Disconnect-Reconnect Page: +0x02 { + {Buffer Full Ratio} i1 + {Buffer Empty Ratio} i1 + {Bus Inactivity Limit} i2 + {Disconnect Time Limit} i2 + {Connect Time Limit} i2 + {Maximum Burst Size} i2 + {Reserved} *t6 + {DTDC} t2 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 +} + +# Peripheral Device Page: +0x09 { + {Interface Identifier} i2 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 +} + + +# DIRECT ACCESS DEVICES +# Caching page: +0x08 { + {Reserved} *t5 + {WCE} t1 + {MF} t1 + {RCD} t1 + {Demand Retention Priority} t4 + {Write Retention Priority} t4 + {Disable Pre-fetch Transfer Length} i2 + {Minumum Pre-fetch} i2 + {Maximum Pre-fetch} i2 + {Maximum Pre-fetch Ceiling} i2 +} + +# Flexible disk page: +0x05 { + {Transfer rate} i2 + {Number of heads} i1 + {Sectors per track} i1 + {Data bytes per sector} i2 + {Number of cylinders} i2 + {Starting cylinder-write precompensation} i2 + {Starting cylinder-reduced write current} i2 + {Drive step rate} i2 + {Drive step pulse width} i1 + {Head settle delay} i2 + {Motor on delay} i1 + {Motor off delay} i1 + {TRDY} t1 + {SSN} t1 + {MO} t1 + {Reserved} *t5 + {Reserved} *t4 + {SPC} t4 + {Write Compensation} i1 + {Head load delay} i1 + {Head unload delay} i1 + {Pin 34} t4 + {Pin 2} t4 + {Pin 4} t4 + {Pin 1} t4 + {Medium rotation rate} i2 + {Reserved} *i1 + {Reserved} *i1 +} + +# Format device page: +0x03 { + {Tracks per Zone} i2 + {Alternate Sectors per Zone} i2 + {Alternate Tracks per Zone} i2 + {Alternate Tracks per Logical Unit} i2 + {Sectors per Track} i2 + {Data Bytes per Physical Sector} i2 + {Interleave} i2 + {Track Skew Factor} i2 + {Cylinder Skew Factor} i2 + {SSEC} t1 + {HSEC} t1 + {RMB} t1 + {SURF} t1 + {Reserved} *t4 +} + +# Medium types supported page: +0x0b { + {Reserved} *i1 + {Reserved} *i1 + {Medium type one supported} i1 + {Medium type two supported} i1 + {Medium type three supported} i1 + {Medium type four supported} i1 +} + +# Notch page (0x0c) + +# Read-Write Error Recovery Page +0x01 { + {AWRE (Auto Write Reallocation Enbld)} t1 + {ARRE (Auto Read Reallocation Enbld)} t1 + {TB (Transfer Block)} t1 + {RC (Read Continuous)} t1 + {EER (Enable Early Recovery)} t1 + {PER (Post Error)} t1 + {DTE (Disable Transfer on Error)} t1 + {DCR (Disable Correction)} t1 + {Read Retry Count} i1 + {Correction Span} i1 + {Head Offset Count} i1 + {Data Strobe Offset Count} i1 + {Reserved} *i1 + {Write Retry Count} i1 + {Reserved} *i1 + {Recovery Time Limit} i2 +} + +# Rigid Disk Drive Geometry Page +0x04 { + {Number of Cylinders} i3 + {Number of Heads} i1 + {Starting Cylinder-Write Precompensation} i3 + {Starting Cylinder-Reduced Write Current} i3 + {Drive Step Rate} i2 + {Landing Zone Cylinder} i3 + {Reserved} *t6 + {RPL} t2 + {Rotational Offset} i1 + {Reserved} *i1 + {Medium Rotation Rate} i2 + {Reserved} *i1 + {Reserved} *i1 +} + +# Verify Error Recovery Page +0x07 { + {Reserved} *t4 + {EER} t1 + {PER} t1 + {DTE} t1 + {DCR} t1 + {Verify Retry Count} i1 + {Verify Correction Span} i1 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 + {Reserved} *i1 + {Verify Recovery Time Limit} i2 +} + +# CD-ROM Audio Control Parameters Page +0x0E { + {Reserved} *t5 + {Immed} t1 + {SOTC} t1 + {Reserved} *t1 + {Reserved} *i2 + {APRVal} t1 + {Reserved} *t3 + {Format of LBAs / sec.} t4 + {Logical Blocks per Second of Audio Playback} i2 + {Reserved} *t4 + {Output Port 0 Channel Selection} t4 + {Output Port 0 Volume} i1 + {Reserved} *t4 + {Output Port 1 Channel Selection} t4 + {Output Port 1 Volume} i1 + {Reserved} *t4 + {Output Port 2 Channel Selection} t4 + {Output Port 2 Volume} i1 + {Reserved} *t4 + {Output Port 3 Channel Selection} t4 + {Output Port 3 Volume} i1 +} |