From 2b302b8741c690e273b2aad119196671c59af52a Mon Sep 17 00:00:00 2001 From: dufault Date: Tue, 24 Jan 1995 12:07:27 +0000 Subject: [PATCH] Submitted by: Julian Elischer and Peter Dufault Obtained from: 1.1.5 "scsi (8)" from 1.1.5, with added support for any command from the command line. --- sbin/scsi/Makefile | 7 + sbin/scsi/scsi.8 | 192 ++++++++++++++++++++ sbin/scsi/scsi.c | 441 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 640 insertions(+) create mode 100644 sbin/scsi/Makefile create mode 100644 sbin/scsi/scsi.8 create mode 100644 sbin/scsi/scsi.c diff --git a/sbin/scsi/Makefile b/sbin/scsi/Makefile new file mode 100644 index 000000000000..07b63216dda0 --- /dev/null +++ b/sbin/scsi/Makefile @@ -0,0 +1,7 @@ +# @(#)Makefile 8.1 (Berkeley) 6/5/93 + +PROG= scsi +MAN8= scsi.8 +LDADD= -lscsi + +.include diff --git a/sbin/scsi/scsi.8 b/sbin/scsi/scsi.8 new file mode 100644 index 000000000000..65ea19409387 --- /dev/null +++ b/sbin/scsi/scsi.8 @@ -0,0 +1,192 @@ +.\" +.\" 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. +.\" +.\" +.\" $Id: scsi.1,v 1.1 1993/11/18 05:05:27 rgrimes Exp $ +.\" +.Dd October 11, 1993 +.Dt SCSI 1 +.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 -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] -c cmd_fmt [arg0 ... argn] \\ # To send 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 -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. +.Pp +.Fr -v +turns on more verbose information. +.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 "-o count out_fmt arg0 ... argn" +.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. +.Sh EXAMPLES +To verify that the device type for the disk /dev/rsd0d is 0 +(direct access device): +.Bd -literal -offset +root# scsi -f /dev/rsd0d -c "12 0 0 0 64 0" -i 64 "*b3 b5" +0 +.Ed +.Pp +To do an inquiry to /dev/rsd2d: +.Bd -literal -offset +root# scsi -f /dev/rsd2d -c "12 0 0 0 64 0" -i 64 "s8 z8 z16 z4" +FUJITSU M2654S-512 010P +.Ed +.Pp +.Sh SEE ALSO +.Xr scsi 4 , +.Xr scsi 3 +.Sh BUGS +.Pp +.Fr out_fmt +support for +.Fr "-o count out_fmt arg0 ... argn" +isn't written yet. You have to specify out_fmt as "-" and read the +data in from standard input. If you want that capability +ask dufault@hda.com. +.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/rsd0d -c "4 0 0 0 0 0" permits anyone who can open +/dev/rsd0d 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 000000000000..603fd8e417ed --- /dev/null +++ b/sbin/scsi/scsi.c @@ -0,0 +1,441 @@ +/* + * 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. + * + * $Id: scsi.c,v 1.1 1993/11/18 05:05:28 rgrimes Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include + +int fd; +int debuglevel; +int dflag,cmd; +int reprobe; +int probe_all; +int verbose = 0; +int bus = -1; /* all busses */ +int targ = -1; /* all targs */ +int lun = 0; /* just lun 0 */ + +usage() +{ + printf( + +"Usage:\n" +"\n" +" scsi -f device -d debug_level # To set debug level\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] -c cmd_fmt [arg0 ... argn] \\ # To send a command...\n" +" -o count out_fmt [arg0 ... argn] # EITHER (for data out)\n" +" -i count in_fmt # OR (for 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; + cmd = 0; + dflag = 0; + while ((ch = getopt(argc, argv, "vpcrf:d:b:t:l:")) != EOF) { + switch (ch) { + case 'p': + probe_all = 1; + break; + case 'r': + reprobe = 1; + break; + case 'c': + cmd = 1; + break; + case 'v': + verbose = 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 = atoi(optarg); + dflag = 1; + break; + case 'b': + bus = atoi(optarg); + break; + case 't': + targ = atoi(optarg); + break; + case 'l': + lun = atoi(optarg); + 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 = atol(h->argv[h->got]); + h->got++; + + 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++; + + return arg; +} + +/* arg_put: "put argument" callback + */ +void arg_put(void *hook, int letter, void *arg, int count, char *name) +{ + if (verbose) + 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'); +} + +/* 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 + */ +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 output = 0; + + 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; + else + { + char *flag = cget(&h, 0); + + if (strcmp(flag, "-o") == 0) + data_phase = out; + else if (strcmp(flag, "-i") == 0) + data_phase = in; + else + { + fprintf(stderr, + "Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag); + usage(); + } + } + + if (data_phase == none) + scsireq->datalen = 0; + else + { + int count; + + if (data_phase == out) + scsireq->flags = SCCMD_WRITE; + else + scsireq->flags = SCCMD_READ; + + count = iget(&h, 0); + scsireq->datalen = count; + + if (count) + { + char *data_fmt; + data_fmt = cget(&h, 0); + + scsireq->databuf = malloc(count); + + if (data_phase == out) + { + if (strcmp(data_fmt, "-") == 0) /* stdin */ + { + if (read(0, scsireq->databuf, count) != count) + { + perror("read"); + exit(errno); + } + } + else /* XXX: Not written yet */ + { + fprintf(stderr, "Can't set up output data using %s.\n", + data_fmt); + exit(-1); + } + } + + if (scsireq_enter(fd, scsireq) == -1) + { + scsi_debug(stderr, -1, scsireq); + exit(errno); + } + + if (data_phase == in) + { + if (strcmp(data_fmt, "-") == 0) /* stdout */ + { + if (write(1, scsireq->databuf, count) != count) + { + perror("write"); + exit(errno); + } + } + else + { + scsireq_decode_visit(scsireq, data_fmt, arg_put, 0); + putchar('\n'); + } + } + } + } +} + +/* do_probe_all: Loop over all SCSI IDs and see if something is + * there. This only does BUS 0 LUN 0. + */ +do_probe_all() +{ + 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); + } +} + +main(int argc, char **argv) +{ + struct scsi_addr scaddr; + + procargs(&argc,&argv); + + if (probe_all) { + do_probe_all(); + } + + if(reprobe) { + scaddr.scbus = bus; + scaddr.target = targ; + scaddr.lun = lun; + + if (ioctl(fd,SCIOCREPROBE,&scaddr) == -1) + perror("ioctl"); + } + + if(dflag) { + if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1) + { + perror("ioctl [SCIODEBUG]"); + exit(1); + } + } + + if (cmd) { + 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); + } +}