diff options
author | joerg <joerg@FreeBSD.org> | 1997-03-06 15:30:06 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 1997-03-06 15:30:06 +0000 |
commit | 22817fb6a16c5bff9c668d25b4bdc66c40524051 (patch) | |
tree | 3d5750a1edb527388a3ec3932a1a7073c03e0988 | |
parent | c7d5f60d3a7e2e3c4da23b157c62504667344438 (diff) | |
download | FreeBSD-src-22817fb6a16c5bff9c668d25b4bdc66c40524051.zip FreeBSD-src-22817fb6a16c5bff9c668d25b4bdc66c40524051.tar.gz |
Import Jason Thorpe's contribution for an updated SCSI media changer
device (now, finally!).
-rw-r--r-- | bin/chio/Makefile | 9 | ||||
-rw-r--r-- | bin/chio/chio.1 | 202 | ||||
-rw-r--r-- | bin/chio/chio.c | 663 | ||||
-rw-r--r-- | bin/chio/defs.h | 57 | ||||
-rw-r--r-- | bin/chio/pathnames.h | 35 | ||||
-rw-r--r-- | sys/scsi/ch.c | 1601 | ||||
-rw-r--r-- | sys/scsi/scsi_changer.h | 438 | ||||
-rw-r--r-- | sys/sys/chio.h | 222 |
8 files changed, 2119 insertions, 1108 deletions
diff --git a/bin/chio/Makefile b/bin/chio/Makefile new file mode 100644 index 0000000..73c1aa1 --- /dev/null +++ b/bin/chio/Makefile @@ -0,0 +1,9 @@ +# $Id: $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= chio +SRCS= chio.c +BINOWN= root +BINMODE=4555 + +.include <bsd.prog.mk> diff --git a/bin/chio/chio.1 b/bin/chio/chio.1 new file mode 100644 index 0000000..22696f4 --- /dev/null +++ b/bin/chio/chio.1 @@ -0,0 +1,202 @@ +.\" $NetBSD: $ +.\" +.\" Copyright (c) 1996 Jason R. Thorpe <thorpej@and.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 acknowledgements: +.\" This product includes software developed by Jason R. Thorpe +.\" for And Communications, http://www.and.com/ +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 April 2, 1996 +.Dt CHIO 1 +.Os +.Sh NAME +.Nm chio +.Nd medium changer control utility +.Sh SYNOPSIS +.Nm chio +.Op Fl f Ar changer +.Ar command +.Ar arg1 +.Ar arg2 +.Oo +.Ar arg3 Oo ... +.Oc +.Oc +.Sh DESCRIPTION +.Nm Chio +is used to control the operation of medium changers, such as those found +in tape and optical disk jukeboxes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f Ar changer +Use the device +.Pa changer +rather than the default device +.Pa /dev/ch0 . +.El +.Pp +The default changer may be overridden by setting the environment variable +.Nm CHANGER +to the desired changer device. +.Pp +A medium changer apparatus is made up of +.Pa elements . +There are four element types: +.Pa picker +(medium transport), +.Pa slot +(storage), +.Pa portal +(import/export), and +.Pa drive +(data transfer). In this command description, the shorthand +.Nm ET +will be used to represent an element type, and +.Nm EU +will be used to represent an element unit. For example, to represent +the first robotic arm in the changer, the ET would be +.Dq picker +and the EU would be +.Dq 0 . +.Pp +.Sh SUPPORTED COMMANDS +.Nm chio move +.Ar <from ET> <from EU> <to ET> <to EU> +.Op Ar inv +.Pp +Moves the media unit from +.Pa <from ET/EU> +to +.Pa <to ET/EU> . +If the optional modifier +.Pa inv +is specified, the media unit will be inverted before insertion. +.Pp +.Nm chio exchange +.Ar <src ET> <src EU> <dst1 ET> <dst1 EU> +.Op Ar <dst2 ET> <dst2 ET> +.Op Ar inv1 +.Op Ar inv2 +.Pp +Performs a media unit exchange operation. The media unit in +.Pa <src ET/EU> +is moved to +.Pa <dst1 ET/EU> +and the media unit previously in +.Pa <dst1 ET/EU> +is moved to +.Pa <dst2 ET/EU> . +In the case of a simple exchange, +.Pa <dst2 ET/EU> +is omitted and the values +.Pa <src ET/EU> +are used in their place. +The optional modifiers +.Pa inv1 +and +.Pa inv2 +specify whether the media units are to be inverted before insertion into +.Pa <dst1 ET/EU> +and +.Pa <dst2 ET/EU> +respecitively. +.Pp +Note that not all medium changers support the +.Nm exchange +operation; The changer must have multiple free pickers or emulate +multiple free pickers with transient storage. +.Pp +.Nm chio position +.Ar <to ET> <to EU> +.Op Ar inv +.Pp +Position the picker in front of the element described by +.Pa <to ET/EU> . +If the optional modifier +.Pa inv +is specified, the media unit will be inverted before insertion. +.Pp +Note that not all changers behave as expected when issued this command. +.Pp +.Nm chio params +.Pp +Report the number of slots, drives, pickers, and portals in the changer, +and which picker unit the changer is currently configured to use. +.Pp +.Nm chio getpicker +.Pp +Report which picker unit the changer is currently configured to use. +.Pp +.Nm chio setpicker +.Ar <unit> +.Pp +Configure the changer to use picker +.Pa <unit> . +.Pp +.Nm chio status +.Op Ar <type> +.Pp +Report the status of all elements in the changer. If +.Pa <type> +is specified, report the status of all elements of type +.Pa <type> . +.Pp +The status bits are defined as follows: +.Bl -tag -width indent +.It Nm FULL +Element contains a media unit. +.It Nm IMPEXP +Media was deposited into element by an outside human operator. +.It Nm EXCEPT +Element is in an abnormal state. +.It Nm ACCESS +Media in this element is accessible by a picker. +.It Nm EXENAB +Element supports passing media (exporting) to an outsite human operator. +.It Nm INENAB +Element supports receiving media (importing) from an outside human operator. +.El +.Pp +.Sh EXAMPLES +.Nm chio move slot 3 drive 0 +.Pp +Moves the media in slot 3 (fourth slot) to drive 0 (first drive). +.Pp +.Nm chio setpicker 2 +Configures the changer to use picker 2 (third picker) for operations. +.Pp +.Sh FILES +/dev/ch0 - default changer device +.Sh SEE ALSO +.Xr mt 1 , +.Xr mount 8 . +.Sh AUTHOR +The +.Nm chio +program and SCSI changer driver were written by Jason R. Thorpe +<thorpej@and.com> for And Communications, http://www.and.com/ diff --git a/bin/chio/chio.c b/bin/chio/chio.c new file mode 100644 index 0000000..51a84e9 --- /dev/null +++ b/bin/chio/chio.c @@ -0,0 +1,663 @@ +/* $Id: $ */ + +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <sys/param.h> +#include <sys/ioctl.h> +#include <sys/chio.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "defs.h" +#include "pathnames.h" + +extern char *__progname; /* from crt0.o */ + +static void usage __P((void)); +static void cleanup __P((void)); +static int parse_element_type __P((char *)); +static int parse_element_unit __P((char *)); +static int parse_special __P((char *)); +static int is_special __P((char *)); +static char *bits_to_string __P((int, const char *)); + +static int do_move __P((char *, int, char **)); +static int do_exchange __P((char *, int, char **)); +static int do_position __P((char *, int, char **)); +static int do_params __P((char *, int, char **)); +static int do_getpicker __P((char *, int, char **)); +static int do_setpicker __P((char *, int, char **)); +static int do_status __P((char *, int, char **)); + +/* Valid changer element types. */ +const struct element_type elements[] = { + { "picker", CHET_MT }, + { "slot", CHET_ST }, + { "portal", CHET_IE }, + { "drive", CHET_DT }, + { NULL, 0 }, +}; + +/* Valid commands. */ +const struct changer_command commands[] = { + { "move", do_move }, + { "exchange", do_exchange }, + { "position", do_position }, + { "params", do_params }, + { "getpicker", do_getpicker }, + { "setpicker", do_setpicker }, + { "status", do_status }, + { NULL, 0 }, +}; + +/* Valid special words. */ +const struct special_word specials[] = { + { "inv", SW_INVERT }, + { "inv1", SW_INVERT1 }, + { "inv2", SW_INVERT2 }, + { NULL, 0 }, +}; + +static int changer_fd; +static char *changer_name; + +int +main(argc, argv) + int argc; + char **argv; +{ + int ch, i; + char *cp; + + while ((ch = getopt(argc, argv, "f:")) != -1) { + switch (ch) { + case 'f': + changer_name = optarg; + break; + + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + /* Get the default changer if not already specified. */ + if (changer_name == NULL) + if ((changer_name = getenv(CHANGER_ENV_VAR)) == NULL) + changer_name = _PATH_CH; + + /* Open the changer device. */ + if ((changer_fd = open(changer_name, O_RDWR, 0600)) == -1) + err(1, "%s: open", changer_name); + + /* Register cleanup function. */ + if (atexit(cleanup)) + err(1, "can't register cleanup function"); + + /* Find the specified command. */ + for (i = 0; commands[i].cc_name != NULL; ++i) + if (strcmp(*argv, commands[i].cc_name) == 0) + break; + if (commands[i].cc_name == NULL) + errx(1, "unknown command: %s", *argv); + + /* Skip over the command name and call handler. */ + ++argv; --argc; + exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); +} + +static int +do_move(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_move cmd; + int val; + + /* + * On a move command, we expect the following: + * + * <from ET> <from EU> <to ET> <to EU> [inv] + * + * where ET == element type and EU == element unit. + */ + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 5) { + warnx("%s: too many arguments", cname); + goto usage; + } + bzero(&cmd, sizeof(cmd)); + + /* <from ET> */ + cmd.cm_fromtype = parse_element_type(*argv); + ++argv; --argc; + + /* <from EU> */ + cmd.cm_fromunit = parse_element_unit(*argv); + ++argv; --argc; + + /* <to ET> */ + cmd.cm_totype = parse_element_type(*argv); + ++argv; --argc; + + /* <to EU> */ + cmd.cm_tounit = parse_element_unit(*argv); + ++argv; --argc; + + /* Deal with optional command modifier. */ + if (argc) { + val = parse_special(*argv); + switch (val) { + case SW_INVERT: + cmd.cm_flags |= CM_INVERT; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOMOVE, (char *)&cmd)) + err(1, "%s: CHIOMOVE", changer_name); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s " + "<from ET> <from EU> <to ET> <to EU> [inv]\n", __progname, cname); + return (1); +} + +static int +do_exchange(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_exchange cmd; + int val; + + /* + * On an exchange command, we expect the following: + * + * <src ET> <src EU> <dst1 ET> <dst1 EU> [<dst2 ET> <dst2 EU>] [inv1] [inv2] + * + * where ET == element type and EU == element unit. + */ + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 8) { + warnx("%s: too many arguments", cname); + goto usage; + } + bzero(&cmd, sizeof(cmd)); + + /* <src ET> */ + cmd.ce_srctype = parse_element_type(*argv); + ++argv; --argc; + + /* <src EU> */ + cmd.ce_srcunit = parse_element_unit(*argv); + ++argv; --argc; + + /* <dst1 ET> */ + cmd.ce_fdsttype = parse_element_type(*argv); + ++argv; --argc; + + /* <dst1 EU> */ + cmd.ce_fdstunit = parse_element_unit(*argv); + ++argv; --argc; + + /* + * If the next token is a special word or there are no more + * arguments, then this is a case of simple exchange. + * dst2 == src. + */ + if ((argc == 0) || is_special(*argv)) { + cmd.ce_sdsttype = cmd.ce_srctype; + cmd.ce_sdstunit = cmd.ce_srcunit; + goto do_special; + } + + /* <dst2 ET> */ + cmd.ce_sdsttype = parse_element_type(*argv); + ++argv; --argc; + + /* <dst2 EU> */ + cmd.ce_sdstunit = parse_element_unit(*argv); + ++argv; --argc; + + do_special: + /* Deal with optional command modifiers. */ + while (argc) { + val = parse_special(*argv); + ++argv; --argc; + switch (val) { + case SW_INVERT1: + cmd.ce_flags |= CE_INVERT1; + break; + + case SW_INVERT2: + cmd.ce_flags |= CE_INVERT2; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOEXCHANGE, (char *)&cmd)) + err(1, "%s: CHIOEXCHANGE", changer_name); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" + " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", + __progname, cname); + return (1); +} + +static int +do_position(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_position cmd; + int val; + + /* + * On a position command, we expect the following: + * + * <to ET> <to EU> [inv] + * + * where ET == element type and EU == element unit. + */ + if (argc < 2) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + bzero(&cmd, sizeof(cmd)); + + /* <to ET> */ + cmd.cp_type = parse_element_type(*argv); + ++argv; --argc; + + /* <to EU> */ + cmd.cp_unit = parse_element_unit(*argv); + ++argv; --argc; + + /* Deal with optional command modifier. */ + if (argc) { + val = parse_special(*argv); + switch (val) { + case SW_INVERT: + cmd.cp_flags |= CP_INVERT; + break; + + default: + errx(1, "%s: inappropriate modifier `%s'", + cname, *argv); + /* NOTREACHED */ + } + } + + /* Send command to changer. */ + if (ioctl(changer_fd, CHIOPOSITION, (char *)&cmd)) + err(1, "%s: CHIOPOSITION", changer_name); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", + __progname, cname); + return (1); +} + +static int +do_params(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_params data; + + /* No arguments to this command. */ + if (argc) { + warnx("%s: no arguements expected", cname); + goto usage; + } + + /* Get params from changer and display them. */ + bzero(&data, sizeof(data)); + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data)) + err(1, "%s: CHIOGPARAMS", changer_name); + + printf("%s: %d slot%s, %d drive%s, %d picker%s", + changer_name, + data.cp_nslots, (data.cp_nslots > 1) ? "s" : "", + data.cp_ndrives, (data.cp_ndrives > 1) ? "s" : "", + data.cp_npickers, (data.cp_npickers > 1) ? "s" : ""); + if (data.cp_nportals) + printf(", %d portal%s", data.cp_nportals, + (data.cp_nportals > 1) ? "s" : ""); + printf("\n%s: current picker: %d\n", changer_name, data.cp_curpicker); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s\n", __progname, cname); + return (1); +} + +static int +do_getpicker(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int picker; + + /* No arguments to this command. */ + if (argc) { + warnx("%s: no arguments expected", cname); + goto usage; + } + + /* Get current picker from changer and display it. */ + if (ioctl(changer_fd, CHIOGPICKER, (char *)&picker)) + err(1, "%s: CHIOGPICKER", changer_name); + + printf("%s: current picker: %d\n", changer_name, picker); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s\n", __progname, cname); + return (1); +} + +static int +do_setpicker(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + int picker; + + if (argc < 1) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 1) { + warnx("%s: too many arguments", cname); + goto usage; + } + + picker = parse_element_unit(*argv); + + /* Set the changer picker. */ + if (ioctl(changer_fd, CHIOSPICKER, (char *)&picker)) + err(1, "%s: CHIOSPICKER", changer_name); + + return (0); + + usage: + fprintf(stderr, "usage: %s %s <picker>\n", __progname, cname); + return (1); +} + +static int +do_status(cname, argc, argv) + char *cname; + int argc; + char **argv; +{ + struct changer_element_status cmd; + struct changer_params data; + u_int8_t *statusp; + int i, count, chet, schet, echet; + char *cmdname, *description; + + /* + * On a status command, we expect the following: + * + * [<ET>] + * + * where ET == element type. + * + * If we get no arguments, we get the status of all + * known element types. + */ + if (argc > 1) { + warnx("%s: too many arguments", cname); + goto usage; + } + + /* + * Get params from changer. Specifically, we need the element + * counts. + */ + bzero(&data, sizeof(data)); + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&data)) + err(1, "%s: CHIOGPARAMS", changer_name); + + if (argc) + schet = echet = parse_element_type(*argv); + else { + schet = CHET_MT; + echet = CHET_DT; + } + + for (chet = schet; chet <= echet; ++chet) { + switch (chet) { + case CHET_MT: + count = data.cp_npickers; + description = "picker"; + break; + + case CHET_ST: + count = data.cp_nslots; + description = "slot"; + break; + + case CHET_IE: + count = data.cp_nportals; + description = "portal"; + break; + + case CHET_DT: + count = data.cp_ndrives; + description = "drive"; + break; + } + + if (count == 0) { + if (argc == 0) + continue; + else { + printf("%s: no %s elements\n", + changer_name, description); + return (0); + } + } + + /* Allocate storage for the status bytes. */ + if ((statusp = (u_int8_t *)malloc(count)) == NULL) + errx(1, "can't allocate status storage"); + + bzero(statusp, count); + bzero(&cmd, sizeof(cmd)); + + cmd.ces_type = chet; + cmd.ces_data = statusp; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cmd)) { + free(statusp); + err(1, "%s: CHIOGSTATUS", changer_name); + } + + /* Dump the status for each element of this type. */ + for (i = 0; i < count; ++i) { + printf("%s %d: %s\n", description, i, + bits_to_string(statusp[i], CESTATUS_BITS)); + } + + free(statusp); + } + + return (0); + + usage: + fprintf(stderr, "usage: %s %s [<element type>]\n", __progname, + cname); + return (1); +} + +static int +parse_element_type(cp) + char *cp; +{ + int i; + + for (i = 0; elements[i].et_name != NULL; ++i) + if (strcmp(elements[i].et_name, cp) == 0) + return (elements[i].et_type); + + errx(1, "invalid element type `%s'", cp); +} + +static int +parse_element_unit(cp) + char *cp; +{ + int i; + char *p; + + i = (int)strtol(cp, &p, 10); + if ((i < 0) || (*p != '\0')) + errx(1, "invalid unit number `%s'", cp); + + return (i); +} + +static int +parse_special(cp) + char *cp; +{ + int val; + + val = is_special(cp); + if (val) + return (val); + + errx(1, "invalid modifier `%s'", cp); +} + +static int +is_special(cp) + char *cp; +{ + int i; + + for (i = 0; specials[i].sw_name != NULL; ++i) + if (strcmp(specials[i].sw_name, cp) == 0) + return (specials[i].sw_value); + + return (0); +} + +static char * +bits_to_string(v, cp) + int v; + const char *cp; +{ + const char *np; + char f, sep, *bp; + static char buf[128]; + + bp = buf; + bzero(buf, sizeof(buf)); + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if ((v & (1 << (f - 1))) == 0) + continue; + bp += sprintf(bp, "%c%.*s", sep, np - cp, cp); + sep = ','; + } + if (sep != '<') + *bp = '>'; + + return (buf); +} + +static void +cleanup() +{ + + /* Simple enough... */ + (void)close(changer_fd); +} + +static void +usage() +{ + + fprintf(stderr, "usage: %s command arg1 arg2 ...\n", __progname); + exit(1); +} diff --git a/bin/chio/defs.h b/bin/chio/defs.h new file mode 100644 index 0000000..5bab57e --- /dev/null +++ b/bin/chio/defs.h @@ -0,0 +1,57 @@ +/* $Id: $ */ + +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + */ + +struct element_type { + char *et_name; /* name; i.e. "picker, "slot", etc. */ + int et_type; /* type number */ +}; + +struct changer_command { + char *cc_name; /* command name */ + /* command handler */ + int (*cc_handler) __P((char *, int, char **)); +}; + +struct special_word { + char *sw_name; /* special word */ + int sw_value; /* token value */ +}; + +/* sw_value */ +#define SW_INVERT 1 /* set "invert media" flag */ +#define SW_INVERT1 2 /* set "invert media 1" flag */ +#define SW_INVERT2 3 /* set "invert media 2" flag */ + +/* Environment variable to check for default changer. */ +#define CHANGER_ENV_VAR "CHANGER" diff --git a/bin/chio/pathnames.h b/bin/chio/pathnames.h new file mode 100644 index 0000000..cdd67fa --- /dev/null +++ b/bin/chio/pathnames.h @@ -0,0 +1,35 @@ +/* $Id: $ */ + +/* + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + */ + +#define _PATH_CH "/dev/ch0" diff --git a/sys/scsi/ch.c b/sys/scsi/ch.c index 1e7469b..93aefd7 100644 --- a/sys/scsi/ch.c +++ b/sys/scsi/ch.c @@ -1,1015 +1,754 @@ -/* - */ +/* $Id: $ */ + /* - * HISTORY - * + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * All rights reserved. + * + * Partially based on an autochanger driver written by Stefan Grefen + * and on an autochanger driver written by the Systems Programming Group + * at the University of Utah Computer Science Department. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- + * 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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 <sys/types.h> -#include <ch.h> - #include <sys/param.h> #include <sys/systm.h> - #include <sys/errno.h> #include <sys/ioctl.h> #include <sys/buf.h> #include <sys/proc.h> #include <sys/user.h> -#include <sys/chio.h> - -#if defined(OSF) -#define SECSIZE 512 -#endif /* defined(OSF) */ +#include <sys/chio.h> +#include <sys/devconf.h> +#include <sys/malloc.h> #include <scsi/scsi_all.h> #include <scsi/scsi_changer.h> #include <scsi/scsiconf.h> +#include "ch.h" + +#define CHRETRIES 2 +#define CHUNIT(x) (minor((x))) +#define CHSETUNIT(x, y) makedev(major(x), y) + +struct ch_softc { + /* + * Human-readable external name. FreeBSD doesn't have a + * generic hook for this, so we make it look NetBSD-like. See + * comment in chattach(). + */ + struct { + char dv_xname[16]; + } sc_dev; + + /* + * Pointer back to the scsi_link. See comment in chattach(). + */ + struct scsi_link *sc_link; + + int sc_picker; /* current picker */ + + /* + * The following information is obtained from the + * element address assignment page. + */ + int sc_firsts[4]; /* firsts, indexed by CHET_* */ + int sc_counts[4]; /* counts, indexed by CHET_* */ + + /* + * The following mask defines the legal combinations + * of elements for the MOVE MEDIUM command. + */ + u_int8_t sc_movemask[4]; + + /* + * As above, but for EXCHANGE MEDIUM. + */ + u_int8_t sc_exchangemask[4]; + + int flags; /* misc. info */ +}; + +/* sc_flags */ +#define CHF_ROTATE 0x01 /* picker can rotate */ -struct scsi_xfer ch_scsi_xfer[NCH]; -int ch_xfer_block_wait[NCH]; +/* + * Autoconfiguration glue + */ +int ch_externalize __P((struct proc *, struct kern_devconf *, + void *, size_t)); +void ch_registerdev __P((int)); + +static struct kern_devconf kdc_ch_template = { + 0, 0, 0, /* filled in by dev_attach */ + "ch", 0, MDDC_SCSI, + ch_externalize, 0, scsi_goaway, SCSI_EXTERNALLEN, + &kdc_scbus0, /* parent */ + 0, /* parentdata */ + DC_UNKNOWN, /* not supported */ +}; + +/* + * SCSI glue. + */ +/* + * Under FreeBSD, this macro sets up a bunch of trampoline + * functions that indirect through the SCSI subsystem. + */ +SCSI_DEVICE_ENTRIES(ch) + +static int chunit __P((dev_t)); +static dev_t chsetunit __P((dev_t, int)); + +/* So, like, why not "int"? */ +errval ch_devopen __P((dev_t, int, int, struct proc *, struct scsi_link *)); +errval ch_devioctl __P((dev_t, int, caddr_t, int, struct proc *, + struct scsi_link *)); +errval ch_devclose __P((dev_t, int, int, struct proc *, struct scsi_link *)); + +struct scsi_device ch_switch = { + NULL, /* (*err_handler) */ + NULL, /* (*start) */ + NULL, /* (*async) */ + NULL, /* (*done) */ + "ch", /* name */ + 0, /* flags */ + { 0, 0 }, /* spare[2] */ + 0, /* link_flags */ + chattach, /* (*attach) */ + "Medium-Changer", /* desc */ + chopen, /* (*open) */ + sizeof(struct ch_softc), /* sizeof_scsi_data */ + T_CHANGER, /* type */ + chunit, /* (*getunit) */ + chsetunit, /* (*setunit) */ + ch_devopen, /* (*dev_open) */ + ch_devioctl, /* (*dev_ioctl) */ + ch_devclose, /* (*dev_close) */ +}; + +int ch_move __P((struct ch_softc *, struct changer_move *)); +int ch_exchange __P((struct ch_softc *, struct changer_exchange *)); +int ch_position __P((struct ch_softc *, struct changer_position *)); +int ch_usergetelemstatus __P((struct ch_softc *, int, u_int8_t *)); +int ch_getelemstatus __P((struct ch_softc *, int, int, caddr_t, size_t)); +int ch_get_params __P((struct ch_softc *, int)); + +/* XXX these should be common SCSI subsystem functions. */ +static __inline u_int32_t +_2btol(bytes) + u_int8_t *bytes; +{ + register u_int32_t rv; -#define PAGESIZ 4096 -#define STQSIZE 4 -#define CH_RETRIES 4 + rv = (bytes[0] << 8) | bytes[1]; + return (rv); +} +static __inline void +_lto2b(val, bytes) + u_int32_t val; + u_int8_t *bytes; +{ -#define MODE(z) ( (minor(z) & 0x0F) ) -#define UNIT(z) ( (minor(z) >> 4) ) + bytes[0] = (val >> 8) & 0xff; + bytes[1] = val & 0xff; +} -#ifndef MACH -#define ESUCCESS 0 -#endif MACH +static __inline void +_lto3b(val, bytes) + u_int32_t val; + u_int8_t *bytes; +{ -int ch_info_valid[NCH]; /* the info about the device is valid */ -int ch_initialized[NCH] ; -int ch_debug = 1; + bytes[0] = (val >> 16) & 0xff; + bytes[1] = (val >> 8) & 0xff; + bytes[2] = val & 0xff; +} -int chattach(); -int ch_done(); -struct ch_data +int +ch_externalize(p, kdc, userp, len) + struct proc *p; + struct kern_devconf *kdc; + void *userp; + size_t len; { - int flags; - struct scsi_switch *sc_sw; /* address of scsi low level switch */ - int ctlr; /* so they know which one we want */ - int targ; /* our scsi target ID */ - int lu; /* out scsi lu */ - short chmo; /* Offset of first CHM */ - short chms; /* No. of CHM */ - short slots; /* No. of Storage Elements */ - short sloto; /* Offset of first SE */ - short imexs; /* No. of Import/Export Slots */ - short imexo; /* Offset of first IM/EX */ - short drives; /* No. of CTS */ - short driveo; /* Offset of first CTS */ - short rot; /* CHM can rotate */ - u_long op_matrix; /* possible opertaions */ - u_short lsterr; /* details of lasterror */ - u_char stor; /* posible Storage locations */ -}ch_data[NCH]; - -#define CH_OPEN 0x01 -#define CH_KNOWN 0x02 - -static int next_ch_unit = 0; -/***********************************************************************\ -* The routine called by the low level scsi routine when it discovers * -* A device suitable for this driver * -\***********************************************************************/ - -int chattach(ctlr,targ,lu,scsi_switch) -struct scsi_switch *scsi_switch; + + return (scsi_externalize(SCSI_LINK(&ch_switch, kdc->kdc_unit), + userp, &len)); +} + +void +ch_registerdev(unit) + int unit; { - int unit,i,stat; - unsigned char *tbl; - - if(scsi_debug & PRINTROUTINES) printf("chattach: "); - /*******************************************************\ - * Check we have the resources for another drive * - \*******************************************************/ - unit = next_ch_unit++; - if( unit >= NCH) - { - printf("Too many scsi changers..(%d > %d) reconfigure kernel",(unit + 1),NCH); - return(0); - } - /*******************************************************\ - * Store information needed to contact our base driver * - \*******************************************************/ - ch_data[unit].sc_sw = scsi_switch; - ch_data[unit].ctlr = ctlr; - ch_data[unit].targ = targ; - ch_data[unit].lu = lu; - - /*******************************************************\ - * Use the subdriver to request information regarding * - * the drive. We cannot use interrupts yet, so the * - * request must specify this. * - \*******************************************************/ - if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/))) - { - printf(" ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s) \n", - unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs); - stat=CH_KNOWN; - } - else - { - printf(" ch%d: scsi changer :- offline\n", unit); - stat=CH_OPEN; - } - ch_initialized[unit] = stat; + struct kern_devconf *kdc; - return; + MALLOC(kdc, struct kern_devconf *, sizeof(struct kern_devconf), + M_TEMP, M_NOWAIT); + if (kdc == NULL) + return; + *kdc = kdc_ch_template; /* struct copy */ + kdc->kdc_unit = unit; + kdc->kdc_description = ch_switch.desc; + dev_attach(kdc); } +errval +chattach(link) + struct scsi_link *link; +{ + struct ch_softc *sc = (struct ch_softc *)(link->sd); + u_int32_t unit = link->dev_unit; + + /* + * FreeBSD doesn't have any common way of carrying + * around a device's external name (i.e. <name><unit>), + * so emulate the structure used by NetBSD to keep the + * diffs lower. + */ + bzero(sc->sc_dev.dv_xname, sizeof(sc->sc_dev.dv_xname)); + sprintf(sc->sc_dev.dv_xname, "%s%d", ch_switch.name, unit); + + /* + * FreeBSD gets "softc" info for a device from the + * scsi_link argument passed to indirect entry point functions. + * NetBSD get scsi_link info from softcs that are + * obtained from indexes passed to direct entry point functions. + * We emulate the NetBSD behavior here to keep the diffs + * lower. + */ + sc->sc_link = link; + + /* + * Get information about the device. Note we can't use + * interrupts yet. + */ + if (ch_get_params(sc, SCSI_NOSLEEP|SCSI_NOMASK)) + printf("offline"); + else { + printf("%d slot%s, %d drive%s, %d picker%s", + sc->sc_counts[CHET_ST], (sc->sc_counts[CHET_ST] > 1) ? + "s" : "", + sc->sc_counts[CHET_DT], (sc->sc_counts[CHET_DT] > 1) ? + "s" : "", + sc->sc_counts[CHET_MT], (sc->sc_counts[CHET_MT] > 1) ? + "s" : ""); + if (sc->sc_counts[CHET_IE]) + printf(", %d portal%s", sc->sc_counts[CHET_IE], + (sc->sc_counts[CHET_IE] > 1) ? "s" : ""); +#ifdef CHANGER_DEBUG + printf("\n"); /* This will probably look ugly ... bummer. */ + printf("%s: move mask: 0x%x 0x%x 0x%x 0x%x\n", + sc->sc_dev.dv_xname, + sc->sc_movemask[CHET_MT], sc->sc_movemask[CHET_ST], + sc->sc_movemask[CHET_IE], sc->sc_movemask[CHET_DT]); + printf("%s: exchange mask: 0x%x 0x%x 0x%x 0x%x\n", + sc->sc_dev.dv_xname, + sc->sc_exchangemask[CHET_MT], sc->sc_exchangemask[CHET_ST], + sc->sc_exchangemask[CHET_IE], sc->sc_exchangemask[CHET_DT]); +#endif /* CHANGER_DEBUG */ + } + /* Default the current picker. */ + sc->sc_picker = sc->sc_firsts[CHET_MT]; -/*******************************************************\ -* open the device. * -\*******************************************************/ -chopen(dev) + /* Register the device with the upper layers. */ + ch_registerdev(unit); + + return (0); +} + +errval +ch_devopen(dev, flags, fmt, p, link) + dev_t dev; + int flags, fmt; + struct proc *p; + struct scsi_link *link; { - int errcode = 0; - int unit,mode; - - unit = UNIT(dev); - mode = MODE(dev); - - /*******************************************************\ - * Check the unit is legal * - \*******************************************************/ - if ( unit >= NCH ) - { - printf("ch %d > %d\n",unit,NCH); - errcode = ENXIO; - return(errcode); - } - /*******************************************************\ - * Only allow one at a time * - \*******************************************************/ - if(ch_data[unit].flags & CH_OPEN) - { - printf("CH%d already open\n",unit); - errcode = ENXIO; - goto bad; - } - - if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS))) - printf("chopen: dev=0x%x (unit %d (of %d))\n" - , dev, unit, NCH); - /*******************************************************\ - * Make sure the device has been initialised * - \*******************************************************/ - - if (!ch_initialized[unit]) - return(ENXIO); - if (ch_initialized[unit]!=CH_KNOWN) { - if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/))) - { - ch_initialized[unit]=CH_KNOWN; - } - else - { - printf(" ch%d: scsi changer :- offline\n", unit); - return(ENXIO); - } - } - /*******************************************************\ - * Check that it is still responding and ok. * - \*******************************************************/ - - if(ch_debug || (scsi_debug & TRACEOPENS)) - printf("device is "); - if (!(ch_req_sense(unit, 0))) - { - errcode = ENXIO; - if(ch_debug || (scsi_debug & TRACEOPENS)) - printf("not responding\n"); + struct ch_softc *sc = (struct ch_softc *)(link->sd); + errval error = 0; + int unit; + + unit = CHUNIT(dev); + + /* + * Only allow one open at a time. + */ + if (link->flags & SDEV_OPEN) + return (EBUSY); + + link->flags |= SDEV_OPEN; + + /* + * Absorb any unit attention errors. Ignore "not ready" + * since this might occur if e.g. a tape isn't actually + * loaded in the drive. + */ + (void)scsi_test_unit_ready(link, SCSI_SILENT); + + /* + * Make sure our parameters are up to date. + */ + if (error = ch_get_params(sc, 0)) goto bad; - } - if(ch_debug || (scsi_debug & TRACEOPENS)) - printf("ok\n"); - if(!(ch_test_ready(unit,0))) - { - printf("ch%d not ready\n",unit); - return(EIO); - } + return (0); - ch_info_valid[unit] = TRUE; + bad: + link->flags &= ~SDEV_OPEN; + return (error); +} - /*******************************************************\ - * Load the physical device parameters * - \*******************************************************/ +errval +ch_devclose(dev, flags, fmt, p, link) + dev_t dev; + int flags, fmt; + struct proc *p; + struct scsi_link *link; +{ - ch_data[unit].flags = CH_OPEN; - return(errcode); -bad: - return(errcode); + link->flags &= ~SDEV_OPEN; + return (0); } -/*******************************************************\ -* close the device.. only called if we are the LAST * -* occurence of an open device * -\*******************************************************/ -chclose(dev) +errval +ch_devioctl(dev, cmd, data, flags, p, link) + dev_t dev; + int cmd; + caddr_t data; + int flags; + struct proc *p; + struct scsi_link *link; { - unsigned char unit,mode; + struct ch_softc *sc = (struct ch_softc *)(link->sd); + caddr_t elemdata; + int error = 0; - unit = UNIT(dev); - mode = MODE(dev); + switch (cmd) { + case CHIOMOVE: + error = ch_move(sc, (struct changer_move *)data); + break; - if(scsi_debug & TRACEOPENS) - printf("Closing device"); - ch_data[unit].flags = 0; - return(0); -} + case CHIOEXCHANGE: + error = ch_exchange(sc, (struct changer_exchange *)data); + break; + case CHIOPOSITION: + error = ch_position(sc, (struct changer_position *)data); + break; + case CHIOGPICKER: + *(int *)data = sc->sc_picker - sc->sc_firsts[CHET_MT]; + break; -/***************************************************************\ -* chstart * -* This routine is also called after other non-queued requests * -* have been made of the scsi driver, to ensure that the queue * -* continues to be drained. * -\***************************************************************/ -/* chstart() is called at splbio */ -chstart(unit) -{ - int drivecount; - register struct buf *bp = 0; - register struct buf *dp; - struct scsi_xfer *xs; - int blkno, nblk; - - - if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit); - /*******************************************************\ - * See if there is a buf to do and we are not already * - * doing one * - \*******************************************************/ - xs=&ch_scsi_xfer[unit]; - if(xs->flags & INUSE) - { - return; /* unit already underway */ - } - if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */ - { - wakeup(&ch_xfer_block_wait[unit]); - return; - } + case CHIOSPICKER: { + int new_picker = *(int *)data; - return; + if (new_picker > (sc->sc_counts[CHET_MT] - 1)) + return (EINVAL); + sc->sc_picker = sc->sc_firsts[CHET_MT] + new_picker; + break; } -} + case CHIOGPARAMS: { + struct changer_params *cp = (struct changer_params *)data; + cp->cp_curpicker = sc->sc_picker - sc->sc_firsts[CHET_MT]; + cp->cp_npickers = sc->sc_counts[CHET_MT]; + cp->cp_nslots = sc->sc_counts[CHET_ST]; + cp->cp_nportals = sc->sc_counts[CHET_IE]; + cp->cp_ndrives = sc->sc_counts[CHET_DT]; + break; } -/*******************************************************\ -* This routine is called by the scsi interrupt when * -* the transfer is complete. -\*******************************************************/ -int ch_done(unit,xs) -int unit; -struct scsi_xfer *xs; -{ - struct buf *bp; - int retval; + case CHIOGSTATUS: { + struct changer_element_status *ces = + (struct changer_element_status *)data; + + error = ch_usergetelemstatus(sc, ces->ces_type, ces->ces_data); + break; } + + /* Implement prevent/allow? */ - if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit); - if (! (xs->flags & INUSE)) - panic("scsi_xfer not in use!"); - wakeup(xs); -} -/*******************************************************\ -* Perform special action on behalf of the user * -* Knows about the internals of this device * -\*******************************************************/ -chioctl(dev, cmd, arg, mode) -dev_t dev; -int cmd; -caddr_t arg; -{ - /* struct ch_cmd_buf *args;*/ - union scsi_cmd *scsi_cmd; - register i,j; - unsigned int opri; - int errcode = 0; - unsigned char unit; - int number,flags,ret; - - /*******************************************************\ - * Find the device that the user is talking about * - \*******************************************************/ - flags = 0; /* give error messages, act on errors etc. */ - unit = UNIT(dev); - - switch(cmd) - { - case CHIOOP: { - struct chop *ch=(struct chop *) arg; - if (ch_debug) - printf("[chtape_chop: %x]\n", ch->ch_op); - - switch ((short)(ch->ch_op)) { - case CHGETPARAM: - ch->u.getparam.chmo= ch_data[unit].chmo; - ch->u.getparam.chms= ch_data[unit].chms; - ch->u.getparam.sloto= ch_data[unit].sloto; - ch->u.getparam.slots= ch_data[unit].slots; - ch->u.getparam.imexo= ch_data[unit].imexo; - ch->u.getparam.imexs= ch_data[unit].imexs; - ch->u.getparam.driveo= ch_data[unit].driveo; - ch->u.getparam.drives= ch_data[unit].drives; - ch->u.getparam.rot= ch_data[unit].rot; - ch->result=0; - return 0; - break; - case CHPOSITION: - return ch_position(unit,&ch->result,ch->u.position.chm, - ch->u.position.to, - flags); - case CHMOVE: - return ch_move(unit,&ch->result, ch->u.position.chm, - ch->u.move.from, ch->u.move.to, - flags); - case CHGETELEM: - return ch_getelem(unit,&ch->result, ch->u.get_elem_stat.type, - ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data, - flags); - default: - return EINVAL; - } - - } default: - return EINVAL; + error = scsi_do_ioctl(dev, cmd, data, flags, p, link); + break; } - return(ret?ESUCCESS:EIO); + return (error); } -ch_getelem(unit,stat,type,from,data,flags) -int unit,from,flags; -short *stat; -char *data; +int +ch_move(sc, cm) + struct ch_softc *sc; + struct changer_move *cm; { - struct scsi_read_element_status scsi_cmd; - char elbuf[32]; - int ret; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = READ_ELEMENT_STATUS; - scsi_cmd.element_type_code=type; - scsi_cmd.starting_element_addr[0]=(from>>8)&0xff; - scsi_cmd.starting_element_addr[1]=from&0xff; - scsi_cmd.number_of_elements[1]=1; - scsi_cmd.allocation_length[2]=32; - - if ((ret=ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(scsi_cmd), - elbuf, - 32, - 100000, - flags) !=ESUCCESS)) { - *stat=ch_data[unit].lsterr; - bcopy(elbuf+16,data,16); - return ret; - } - bcopy(elbuf+16,data,16); /*Just a hack sh */ - return ret; + struct scsi_move_medium cmd; + u_int16_t fromelem, toelem; + + /* + * Check arguments. + */ + if ((cm->cm_fromtype > CHET_DT) || (cm->cm_totype > CHET_DT)) + return (EINVAL); + if ((cm->cm_fromunit > (sc->sc_counts[cm->cm_fromtype] - 1)) || + (cm->cm_tounit > (sc->sc_counts[cm->cm_totype] - 1))) + return (ENODEV); + + /* + * Check the request against the changer's capabilities. + */ + if ((sc->sc_movemask[cm->cm_fromtype] & (1 << cm->cm_totype)) == 0) + return (EINVAL); + + /* + * Calculate the source and destination elements. + */ + fromelem = sc->sc_firsts[cm->cm_fromtype] + cm->cm_fromunit; + toelem = sc->sc_firsts[cm->cm_totype] + cm->cm_tounit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = MOVE_MEDIUM; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(fromelem, cmd.src); + _lto2b(toelem, cmd.dst); + if (cm->cm_flags & CM_INVERT) + cmd.flags |= MOVE_MEDIUM_INVERT; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } -ch_move(unit,stat,chm,from,to,flags) -int unit,chm,from,to,flags; -short *stat; +int +ch_exchange(sc, ce) + struct ch_softc *sc; + struct changer_exchange *ce; { - struct scsi_move_medium scsi_cmd; - int ret; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = MOVE_MEDIUM; - scsi_cmd.transport_element_address[0]=(chm>>8)&0xff; - scsi_cmd.transport_element_address[1]=chm&0xff; - scsi_cmd.source_address[0]=(from>>8)&0xff; - scsi_cmd.source_address[1]=from&0xff; - scsi_cmd.destination_address[0]=(to>>8)&0xff; - scsi_cmd.destination_address[1]=to&0xff; - scsi_cmd.invert=(chm&CH_INVERT)?1:0; - if ((ret=ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(scsi_cmd), - NULL, - 0, - 100000, - flags) !=ESUCCESS)) { - *stat=ch_data[unit].lsterr; - return ret; - } - return ret; + struct scsi_exchange_medium cmd; + u_int16_t src, dst1, dst2; + + /* + * Check arguments. + */ + if ((ce->ce_srctype > CHET_DT) || (ce->ce_fdsttype > CHET_DT) || + (ce->ce_sdsttype > CHET_DT)) + return (EINVAL); + if ((ce->ce_srcunit > (sc->sc_counts[ce->ce_srctype] - 1)) || + (ce->ce_fdstunit > (sc->sc_counts[ce->ce_fdsttype] - 1)) || + (ce->ce_sdstunit > (sc->sc_counts[ce->ce_sdsttype] - 1))) + return (ENODEV); + + /* + * Check the request against the changer's capabilities. + */ + if (((sc->sc_exchangemask[ce->ce_srctype] & + (1 << ce->ce_fdsttype)) == 0) || + ((sc->sc_exchangemask[ce->ce_fdsttype] & + (1 << ce->ce_sdsttype)) == 0)) + return (EINVAL); + + /* + * Calculate the source and destination elements. + */ + src = sc->sc_firsts[ce->ce_srctype] + ce->ce_srcunit; + dst1 = sc->sc_firsts[ce->ce_fdsttype] + ce->ce_fdstunit; + dst2 = sc->sc_firsts[ce->ce_sdsttype] + ce->ce_sdstunit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = EXCHANGE_MEDIUM; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(src, cmd.src); + _lto2b(dst1, cmd.fdst); + _lto2b(dst2, cmd.sdst); + if (ce->ce_flags & CE_INVERT1) + cmd.flags |= EXCHANGE_MEDIUM_INV1; + if (ce->ce_flags & CE_INVERT2) + cmd.flags |= EXCHANGE_MEDIUM_INV2; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } -ch_position(unit,stat,chm,to,flags) -int unit,chm,to,flags; -short *stat; +int +ch_position(sc, cp) + struct ch_softc *sc; + struct changer_position *cp; { - struct scsi_position_to_element scsi_cmd; - int ret; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = POSITION_TO_ELEMENT; - scsi_cmd.transport_element_address[0]=(chm>>8)&0xff; - scsi_cmd.transport_element_address[1]=chm&0xff; - scsi_cmd.source_address[0]=(to>>8)&0xff; - scsi_cmd.source_address[1]=to&0xff; - scsi_cmd.invert=(chm&CH_INVERT)?1:0; - if ((ret=ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(scsi_cmd), - NULL, - 0, - 100000, - flags) !=ESUCCESS)) { - *stat=ch_data[unit].lsterr; - return ret; - } - return ret; + struct scsi_position_to_element cmd; + u_int16_t dst; + + /* + * Check arguments. + */ + if (cp->cp_type > CHET_DT) + return (EINVAL); + if (cp->cp_unit > (sc->sc_counts[cp->cp_type] - 1)) + return (ENODEV); + + /* + * Calculate the destination element. + */ + dst = sc->sc_firsts[cp->cp_type] + cp->cp_unit; + + /* + * Build the SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = POSITION_TO_ELEMENT; + _lto2b(sc->sc_picker, cmd.tea); + _lto2b(dst, cmd.dst); + if (cp->cp_flags & CP_INVERT) + cmd.flags |= POSITION_TO_ELEMENT_INVERT; + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), NULL, 0, CHRETRIES, 100000, NULL, 0)); } -/*******************************************************\ -* Check with the device that it is ok, (via scsi driver)* -\*******************************************************/ -ch_req_sense(unit, flags) -int flags; +/* + * Perform a READ ELEMENT STATUS on behalf of the user, and return to + * the user only the data the user is interested in (i.e. an array of + * flags bytes). + */ +int +ch_usergetelemstatus(sc, chet, uptr) + struct ch_softc *sc; + int chet; + u_int8_t *uptr; { - struct scsi_sense_data sense; - struct scsi_sense scsi_cmd; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = REQUEST_SENSE; - scsi_cmd.length = sizeof(sense); - - if (ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(struct scsi_sense), - &sense, - sizeof(sense), - 100000, - flags | SCSI_DATA_IN) != 0) - { - return(FALSE); + struct read_element_status_header *st_hdr; + struct read_element_status_page_header *pg_hdr; + struct read_element_status_descriptor *desc; + caddr_t data = NULL; + size_t size, desclen; + int avail, i, error = 0; + u_int8_t *user_data = NULL; + + /* + * If there are no elements of the requested type in the changer, + * the request is invalid. + */ + if (sc->sc_counts[chet] == 0) + return (EINVAL); + + /* + * Request one descriptor for the given element type. This + * is used to determine the size of the descriptor so that + * we can allocate enough storage for all of them. We assume + * that the first one can fit into 1k. + */ + data = (caddr_t)malloc(1024, M_DEVBUF, M_WAITOK); + if (error = ch_getelemstatus(sc, sc->sc_firsts[chet], 1, data, 1024)) + goto done; + + st_hdr = (struct read_element_status_header *)data; + pg_hdr = (struct read_element_status_page_header *)((u_long)st_hdr + + sizeof(struct read_element_status_header)); + desclen = _2btol(pg_hdr->edl); + + size = sizeof(struct read_element_status_header) + + sizeof(struct read_element_status_page_header) + + (desclen * sc->sc_counts[chet]); + + /* + * Reallocate storage for descriptors and get them from the + * device. + */ + free(data, M_DEVBUF); + data = (caddr_t)malloc(size, M_DEVBUF, M_WAITOK); + if (error = ch_getelemstatus(sc, sc->sc_firsts[chet], + sc->sc_counts[chet], data, size)) + goto done; + + /* + * Fill in the user status array. + */ + st_hdr = (struct read_element_status_header *)data; + avail = _2btol(st_hdr->count); + if (avail != sc->sc_counts[chet]) + printf("%s: warning, READ ELEMENT STATUS avail != count\n", + sc->sc_dev.dv_xname); + + user_data = (u_int8_t *)malloc(avail, M_DEVBUF, M_WAITOK); + + desc = (struct read_element_status_descriptor *)((u_long)data + + sizeof(struct read_element_status_header) + + sizeof(struct read_element_status_page_header)); + for (i = 0; i < avail; ++i) { + user_data[i] = desc->flags1; + (u_long)desc += desclen; } - else - return(TRUE); + + /* Copy flags array out to userspace. */ + error = copyout(user_data, uptr, avail); + + done: + if (data != NULL) + free(data, M_DEVBUF); + if (user_data != NULL) + free(user_data, M_DEVBUF); + return (error); } -/*******************************************************\ -* Get scsi driver to send a "are you ready" command * -\*******************************************************/ -ch_test_ready(unit,flags) -int unit,flags; +int +ch_getelemstatus(sc, first, count, data, datalen) + struct ch_softc *sc; + int first, count; + caddr_t data; + size_t datalen; { - struct scsi_test_unit_ready scsi_cmd; - - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = TEST_UNIT_READY; - - if (ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(struct scsi_test_unit_ready), - 0, - 0, - 100000, - flags) != 0) { - return(FALSE); - } else - return(TRUE); + struct scsi_read_element_status cmd; + + /* + * Build SCSI command. + */ + bzero(&cmd, sizeof(cmd)); + cmd.opcode = READ_ELEMENT_STATUS; + _lto2b(first, cmd.sea); + _lto2b(count, cmd.count); + _lto3b(datalen, cmd.len); + + /* + * Send command to changer. + */ + return (scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)data, datalen, CHRETRIES, 100000, NULL, 0)); } -#ifdef __STDC__ -#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) -#else -#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 ) -#endif - -/*******************************************************\ -* Get the scsi driver to send a full inquiry to the * -* device and use the results to fill out the global * -* parameter structure. * -\*******************************************************/ -ch_mode_sense(unit, flags) -int unit,flags; +/* + * Ask the device about itself and fill in the parameters in our + * softc. + */ +int +ch_get_params(sc, scsiflags) + struct ch_softc *sc; + int scsiflags; { - struct scsi_mode_sense scsi_cmd; - u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of */ - /* missing block descriptor */ - u_char *b; - int i,l; - - /*******************************************************\ - * First check if we have it all loaded * - \*******************************************************/ - if (ch_info_valid[unit]==CH_KNOWN) return(TRUE); - /*******************************************************\ - * First do a mode sense * - \*******************************************************/ - ch_info_valid[unit] &= ~CH_KNOWN; - for(l=1;l>=0;l--) { - bzero(&scsi_cmd, sizeof(scsi_cmd)); - scsi_cmd.op_code = MODE_SENSE; - scsi_cmd.dbd = l; - scsi_cmd.page_code = 0x3f; /* All Pages */ - scsi_cmd.length = sizeof(scsi_sense); - /*******************************************************\ - * do the command, but we don't need the results * - * just print them for our interest's sake * - \*******************************************************/ - if (ch_scsi_cmd(unit, - &scsi_cmd, - sizeof(struct scsi_mode_sense), - &scsi_sense, - sizeof(scsi_sense), - 5000, - flags | SCSI_DATA_IN) == 0) { - ch_info_valid[unit] = CH_KNOWN; - break; - } - } - if (ch_info_valid[unit]!=CH_KNOWN) { - if(!(flags & SCSI_SILENT)) - printf("could not mode sense for unit %d\n", unit); - return(FALSE); - } - l=scsi_sense[0]-3; - b=&scsi_sense[4]; - /*****************************\ - * To avoid alignment problems * - \*****************************/ -/*FIX THIS FOR MSB */ -#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2 -#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4 -#if 0 - printf("\nmode_sense %d\n",l); - for(i=0;i<l+4;i++) { - printf("%x%c",scsi_sense[i],i%8==7?'\n':':'); + struct scsi_mode_sense cmd; + struct scsi_mode_sense_data { + struct scsi_mode_header header; + union { + struct page_element_address_assignment ea; + struct page_transport_geometry_parameters tg; + struct page_device_capabilities cap; + } pages; + } sense_data; + int error, from; + u_int8_t *moves, *exchanges; + + /* + * Grab info from the element address assignment page. + */ + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.op_code = MODE_SENSE; + cmd.byte2 |= 0x08; /* disable block descriptors */ + cmd.page = 0x1d; + cmd.length = (sizeof(sense_data) & 0xff); + error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, + 6000, NULL, scsiflags | SCSI_DATA_IN); + if (error) { + printf("%s: could not sense element address page\n"); + return (error); } - printf("\n"); -#endif - for(i=0;i<l;) { - int pc=(*b++)&0x3f; - int pl=*b++; - u_char *bb=b; - switch(pc) { - case 0x1d: - ch_data[unit].chmo =p2copy(bb); - ch_data[unit].chms =p2copy(bb); - ch_data[unit].sloto =p2copy(bb); - ch_data[unit].slots =p2copy(bb); - ch_data[unit].imexo =p2copy(bb); - ch_data[unit].imexs =p2copy(bb); - ch_data[unit].driveo =p2copy(bb); - ch_data[unit].drives =p2copy(bb); - break; - case 0x1e: - ch_data[unit].rot = (*b)&1; - break; - case 0x1f: - ch_data[unit].stor = *b&0xf; - bb+=2; - ch_data[unit].stor =p4copy(bb); - break; - default: - break; - } - b+=pl; - i+=pl+2; - } - if (ch_debug) - { - printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n", - unit, - ch_data[unit].chmo, - ch_data[unit].chms, - ch_data[unit].sloto, - ch_data[unit].slots, - ch_data[unit].imexo, - ch_data[unit].imexs, - ch_data[unit].driveo, - ch_data[unit].drives, - ch_data[unit].rot?"can":"can't"); - } - return(TRUE); -} -/*******************************************************\ -* ask the scsi driver to perform a command for us. * -* Call it through the switch table, and tell it which * -* sub-unit we want, and what target and lu we wish to * -* talk to. Also tell it where to find the command * -* how long int is. * -* Also tell it where to read/write the data, and how * -* long the data is supposed to be * -\*******************************************************/ -int ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags) - -int unit,flags; -struct scsi_generic *scsi_cmd; -int cmdlen; -int timeout; -u_char *data_addr; -int datalen; -{ - struct scsi_xfer *xs; - int retval; - int s; - - if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x", - unit,scsi_cmd->opcode); - if(ch_data[unit].sc_sw) /* If we have a scsi driver */ - { - - xs = &(ch_scsi_xfer[unit]); - if(!(flags & SCSI_NOMASK)) - s = splbio(); - ch_xfer_block_wait[unit]++; /* there is someone waiting */ - while (xs->flags & INUSE) - { - sleep(&ch_xfer_block_wait[unit],PRIBIO+1); - } - ch_xfer_block_wait[unit]--; - xs->flags = INUSE; - if(!(flags & SCSI_NOMASK)) - splx(s); - - /*******************************************************\ - * Fill out the scsi_xfer structure * - \*******************************************************/ - xs->flags |= flags; - xs->adapter = ch_data[unit].ctlr; - xs->targ = ch_data[unit].targ; - xs->lu = ch_data[unit].lu; - xs->retries = CH_RETRIES; - xs->timeout = timeout; - xs->cmd = scsi_cmd; - xs->cmdlen = cmdlen; - xs->data = data_addr; - xs->datalen = datalen; - xs->resid = datalen; - xs->when_done = (flags & SCSI_NOMASK) - ?(int (*)())0 - :ch_done; - xs->done_arg = unit; - xs->done_arg2 = (int)xs; -retry: xs->error = XS_NOERROR; - xs->bp = 0; - ch_data[unit].lsterr=0; - retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs); - switch(retval) - { - case SUCCESSFULLY_QUEUED: - while(!(xs->flags & ITSDONE)) - sleep(xs,PRIBIO+1); - - case HAD_ERROR: - case COMPLETE: - switch(xs->error) - { - case XS_NOERROR: - retval = ESUCCESS; - break; - case XS_SENSE: - retval = (ch_interpret_sense(unit,xs)); - break; - case XS_DRIVER_STUFFUP: - retval = EIO; - break; - case XS_TIMEOUT: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - case XS_BUSY: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - default: - retval = EIO; - printf("st%d: unknown error category from scsi driver\n" - ,unit); - break; - } - break; - case TRY_AGAIN_LATER: - if(xs->retries-- ) - { - xs->flags &= ~ITSDONE; - goto retry; - } - retval = EIO; - break; - default: - retval = EIO; - } - xs->flags = 0; /* it's free! */ - chstart(unit); + sc->sc_firsts[CHET_MT] = _2btol(sense_data.pages.ea.mtea); + sc->sc_counts[CHET_MT] = _2btol(sense_data.pages.ea.nmte); + sc->sc_firsts[CHET_ST] = _2btol(sense_data.pages.ea.fsea); + sc->sc_counts[CHET_ST] = _2btol(sense_data.pages.ea.nse); + sc->sc_firsts[CHET_IE] = _2btol(sense_data.pages.ea.fieea); + sc->sc_counts[CHET_IE] = _2btol(sense_data.pages.ea.niee); + sc->sc_firsts[CHET_DT] = _2btol(sense_data.pages.ea.fdtea); + sc->sc_counts[CHET_DT] = _2btol(sense_data.pages.ea.ndte); + + /* XXX ask for page trasport geom */ + + /* + * Grab info from the capabilities page. + */ + bzero(&cmd, sizeof(cmd)); + bzero(&sense_data, sizeof(sense_data)); + cmd.op_code = MODE_SENSE; + cmd.byte2 |= 0x08; /* disable block descriptors */ + cmd.page = 0x1f; + cmd.length = (sizeof(sense_data) & 0xff); + error = scsi_scsi_cmd(sc->sc_link, (struct scsi_generic *)&cmd, + sizeof(cmd), (u_char *)&sense_data, sizeof(sense_data), CHRETRIES, + 6000, NULL, scsiflags | SCSI_DATA_IN); + if (error) { + printf("%s: could not sense capabilities page\n"); + return (error); } - else - { - printf("chd: not set up\n",unit); - return(EINVAL); + + bzero(sc->sc_movemask, sizeof(sc->sc_movemask)); + bzero(sc->sc_exchangemask, sizeof(sc->sc_exchangemask)); + moves = &sense_data.pages.cap.move_from_mt; + exchanges = &sense_data.pages.cap.exchange_with_mt; + for (from = CHET_MT; from <= CHET_DT; ++from) { + sc->sc_movemask[from] = moves[from]; + sc->sc_exchangemask[from] = exchanges[from]; } - return(retval); + + sc->sc_link->flags |= SDEV_MEDIA_LOADED; + return (0); } -/***************************************************************\ -* Look at the returned sense and act on the error and detirmine * -* The unix error number to pass back... (0 = report no error) * -\***************************************************************/ - -int ch_interpret_sense(unit,xs) -int unit; -struct scsi_xfer *xs; + +static int +chunit(dev) + dev_t dev; { - struct scsi_sense_data *sense; - int key; - int silent = xs->flags & SCSI_SILENT; - - /***************************************************************\ - * If errors are ok, report a success * - \***************************************************************/ - if(xs->flags & SCSI_ERR_OK) return(ESUCCESS); - - /***************************************************************\ - * Get the sense fields and work out what CLASS * - \***************************************************************/ - sense = &(xs->sense); - switch(sense->error_class) - { - /***************************************************************\ - * If it's class 7, use the extended stuff and interpret the key * - \***************************************************************/ - case 7: - { - key=sense->ext.extended.sense_key; - if(sense->ext.extended.ili) - if(!silent) - { - printf("length error "); - } - if(sense->valid) - xs->resid = ntohl(*((long *)sense->ext.extended.info)); - if(xs->bp) - { - xs->bp->b_flags |= B_ERROR; - return(ESUCCESS); - } - if(sense->ext.extended.eom) - if(!silent) printf("end of medium "); - if(sense->ext.extended.filemark) - if(!silent) printf("filemark "); - if(ch_debug) - { - printf("code%x class%x valid%x\n" - ,sense->error_code - ,sense->error_class - ,sense->valid); - printf("seg%x key%x ili%x eom%x fmark%x\n" - ,sense->ext.extended.segment - ,sense->ext.extended.sense_key - ,sense->ext.extended.ili - ,sense->ext.extended.eom - ,sense->ext.extended.filemark); - printf("info: %x %x %x %x followed by %d extra bytes\n" - ,sense->ext.extended.info[0] - ,sense->ext.extended.info[1] - ,sense->ext.extended.info[2] - ,sense->ext.extended.info[3] - ,sense->ext.extended.extra_len); - printf("extra: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n" - ,sense->ext.extended.extra_bytes[0] - ,sense->ext.extended.extra_bytes[1] - ,sense->ext.extended.extra_bytes[2] - ,sense->ext.extended.extra_bytes[3] - ,sense->ext.extended.extra_bytes[4] - ,sense->ext.extended.extra_bytes[5] - ,sense->ext.extended.extra_bytes[6] - ,sense->ext.extended.extra_bytes[7] - ,sense->ext.extended.extra_bytes[8] - ,sense->ext.extended.extra_bytes[9] - ,sense->ext.extended.extra_bytes[10] - ,sense->ext.extended.extra_bytes[11] - ,sense->ext.extended.extra_bytes[12] - ,sense->ext.extended.extra_bytes[13] - ,sense->ext.extended.extra_bytes[14] - ,sense->ext.extended.extra_bytes[15]); - - } - switch(key) - { - case 0x0: - return(ESUCCESS); - case 0x1: - if(!silent) - { - printf("st%d: soft error(corrected) ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(ESUCCESS); - case 0x2: - if(!silent) printf("st%d: not ready\n ", unit); - ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; - return(ENODEV); - case 0x3: - if(!silent) - { - printf("st%d: medium error ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(EIO); - case 0x4: - if(!silent) printf("st%d: non-media hardware failure\n ", - unit); - ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; - return(EIO); - case 0x5: - if(!silent) printf("st%d: illegal request\n ", unit); - ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; - return(EINVAL); - case 0x6: - if(!silent) printf("st%d: Unit attention.\n ", unit); - ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; - ch_info_valid[unit] = FALSE; - if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */ - return(EIO); - else - return(ESUCCESS); - case 0x7: - if(!silent) - { - printf("st%d: attempted protection violation " - , unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(EACCES); - case 0x8: - if(!silent) - { - printf("st%d: block wrong state (worm)\n " - , unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(EIO); - case 0x9: - if(!silent) printf("st%d: vendor unique\n", - unit); - return(EIO); - case 0xa: - if(!silent) printf("st%d: copy aborted\n ", - unit); - return(EIO); - case 0xb: - if(!silent) printf("st%d: command aborted\n ", - unit); - ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)| - sense->ext.extended.info[13] ; - return(EIO); - case 0xc: - if(!silent) - { - printf("st%d: search returned\n ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(ESUCCESS); - case 0xd: - if(!silent) printf("st%d: volume overflow\n ", - unit); - return(ENOSPC); - case 0xe: - if(!silent) - { - printf("st%d: verify miscompare\n ", unit); - if(sense->valid) - { - printf("block no. %d (decimal)\n", - (sense->ext.extended.info[0] <<24)| - (sense->ext.extended.info[1] <<16)| - (sense->ext.extended.info[2] <<8)| - (sense->ext.extended.info[3] )); - } - else - { - printf("\n"); - } - } - return(EIO); - case 0xf: - if(!silent) printf("st%d: unknown error key\n ", - unit); - return(EIO); - } - break; - } - /***************************************************************\ - * If it's NOT class 7, just report it. * - \***************************************************************/ - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - { - if(!silent) printf("st%d: error class %d code %d\n", - unit, - sense->error_class, - sense->error_code); - if(sense->valid) - if(!silent) printf("block no. %d (decimal)\n", - (sense->ext.unextended.blockhi <<16), - + (sense->ext.unextended.blockmed <<8), - + (sense->ext.unextended.blocklow )); - } - return(EIO); - } -} + return (CHUNIT(dev)); +} +static dev_t +chsetunit(dev, unit) + dev_t dev; + int unit; +{ + return (CHSETUNIT(dev, unit)); +} diff --git a/sys/scsi/scsi_changer.h b/sys/scsi/scsi_changer.h index 63a5b13..d5c202b 100644 --- a/sys/scsi/scsi_changer.h +++ b/sys/scsi/scsi_changer.h @@ -1,13 +1,39 @@ +/* $Id: $ */ + /* - * HISTORY + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> + * All rights reserved. + * + * Partially based on an autochanger driver written by Stefan Grefen + * and on an autochanger driver written by the Systems Programming Group + * at the University of Utah Computer Science Department. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- + * 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 acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - * 16 Feb 93 Julian Elischer ADDED for SCSI system - * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. */ /* @@ -15,7 +41,8 @@ */ /* - * Written by Stefan Grefen (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com) + * Partially derived from software written by Stefan Grefen + * (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com) * based on the SCSI System by written Julian Elischer (julian@tfs.com) * for TRW Financial Systems. * @@ -29,90 +56,345 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * + * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 */ +#ifndef _SCSI_SCSI_CHANGER_H +#define _SCSI_SCSI_CHANGER_H 1 + /* - * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992 + * SCSI command format */ /* - * SCSI command format + * Exchange the medium in the source element with the medium + * located at the destination element. + */ +struct scsi_exchange_medium { + u_int8_t opcode; +#define EXCHANGE_MEDIUM 0xa6 + u_int8_t byte2; + u_int8_t tea[2]; /* transport element address */ + u_int8_t src[2]; /* source address */ + u_int8_t fdst[2]; /* first destination address */ + u_int8_t sdst[2]; /* second destination address */ + u_int8_t flags; +#define EXCHANGE_MEDIUM_INV1 0x01 +#define EXCHANGE_MEDIUM_INV2 0x02 + u_int8_t control; +}; + +/* + * Cause the medium changer to check all elements for medium and any + * other status relevant to the element. + */ +struct scsi_initialize_elememt_status { + u_int8_t opcode; +#define INITIALIZE_ELEMENT_STATUS 0x07 + u_int8_t byte2; + u_int8_t reserved[3]; + u_int8_t control; +}; + +/* + * Request the changer to move a unit of media from the source element + * to the destination element. */ -struct scsi_read_element_status -{ - u_char op_code; - u_char element_type_code:4; - u_char voltag:1; - u_char lun:3; - u_char starting_element_addr[2]; - u_char number_of_elements[2]; - u_char resv1; - u_char allocation_length[3]; - u_char resv2; - u_char link:1; - u_char flag:1; - u_char :6; +struct scsi_move_medium { + u_int8_t opcode; +#define MOVE_MEDIUM 0xa5 + u_int8_t byte2; + u_int8_t tea[2]; /* transport element address */ + u_int8_t src[2]; /* source element address */ + u_int8_t dst[2]; /* destination element address */ + u_int8_t reserved[2]; + u_int8_t flags; +#define MOVE_MEDIUM_INVERT 0x01 + u_int8_t control; }; -#define RE_ALL_ELEMENTS 0 -#define RE_MEDIUM_TRANSPORT_ELEMENT 1 -#define RE_STORAGE_ELEMENT 2 -#define RE_IMPORT_EXPORT 3 -#define RE_DATA_TRANSFER_ELEMENT 4 - -struct scsi_move_medium -{ - u_char op_code; - u_char :5; - u_char lun:3; - u_char transport_element_address[2]; - u_char source_address[2]; - u_char destination_address[2]; - u_char rsvd[2]; - u_char invert:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; + +/* + * Position the specified transport element (picker) in front of + * the destination element specified. + */ +struct scsi_position_to_element { + u_int8_t opcode; +#define POSITION_TO_ELEMENT 0x2b + u_int8_t byte2; + u_int8_t tea[2]; /* transport element address */ + u_int8_t dst[2]; /* destination element address */ + u_int8_t reserved[2]; + u_int8_t flags; +#define POSITION_TO_ELEMENT_INVERT 0x01 + u_int8_t control; +}; + +/* + * Request that the changer report the status of its internal elements. + */ +struct scsi_read_element_status { + u_int8_t opcode; +#define READ_ELEMENT_STATUS 0xb8 + u_int8_t byte2; +#define READ_ELEMENT_STATUS_VOLTAG 0x10 /* report volume tag info */ + /* ...next 4 bits are an element type code... */ + u_int8_t sea[2]; /* starting element address */ + u_int8_t count[2]; /* number of elements */ + u_int8_t reserved0; + u_int8_t len[3]; /* length of data buffer */ + u_int8_t reserved1; + u_int8_t control; +}; + +struct scsi_request_volume_element_address { + u_int8_t opcode; +#define REQUEST_VOLUME_ELEMENT_ADDRESS 0xb5 + u_int8_t byte2; +#define REQUEST_VOLUME_ELEMENT_ADDRESS_VOLTAG 0x10 + /* ...next 4 bits are an element type code... */ + u_int8_t eaddr[2]; /* element address */ + u_int8_t count[2]; /* number of elements */ + u_int8_t reserved0; + u_int8_t len[3]; /* length of data buffer */ + u_int8_t reserved1; + u_int8_t control; +}; + +/* XXX scsi_release */ + +/* + * Data returned by READ ELEMENT STATUS consists of an 8-byte header + * followed by one or more read_element_status_pages. + */ +struct read_element_status_header { + u_int8_t fear[2]; /* first element address reported */ + u_int8_t count[2]; /* number of elements available */ + u_int8_t reserved; + u_int8_t nbytes[3]; /* byte count of all pages */ +}; + +struct read_element_status_page_header { + u_int8_t type; /* element type code; see type codes below */ + u_int8_t flags; +#define READ_ELEMENT_STATUS_AVOLTAG 0x40 +#define READ_ELEMENT_STATUS_PVOLTAG 0x80 + u_int8_t edl[2]; /* element descriptor length */ + u_int8_t reserved; + u_int8_t nbytes[3]; /* byte count of all descriptors */ +}; + +struct read_element_status_descriptor { + u_int8_t eaddr[2]; /* element address */ + u_int8_t flags1; + +#define READ_ELEMENT_STATUS_FULL 0x01 +#define READ_ELEMENT_STATUS_IMPEXP 0x02 +#define READ_ELEMENT_STATUS_EXCEPT 0x04 +#define READ_ELEMENT_STATUS_ACCESS 0x08 +#define READ_ELEMENT_STATUS_EXENAB 0x10 +#define READ_ELEMENT_STATUS_INENAB 0x20 + +#define READ_ELEMENT_STATUS_MT_MASK1 0x05 +#define READ_ELEMENT_STATUS_ST_MASK1 0x0c +#define READ_ELEMENT_STATUS_IE_MASK1 0x3f +#define READ_ELEMENT_STATUS_DT_MASK1 0x0c + + u_int8_t reserved0; + u_int8_t sense_code; + u_int8_t sense_qual; + + /* + * dt_scsi_flags and dt_scsi_addr are valid only on data transport + * elements. These bytes are undefined for all other element types. + */ + u_int8_t dt_scsi_flags; + +#define READ_ELEMENT_STATUS_DT_LUNMASK 0x07 +#define READ_ELEMENT_STATUS_DT_LUVALID 0x10 +#define READ_ELEMENT_STATUS_DT_IDVALID 0x20 +#define READ_ELEMENT_STATUS_DT_NOTBUS 0x80 + + u_int8_t dt_scsi_addr; + + u_int8_t reserved1; + + u_int8_t flags2; +#define READ_ELEMENT_STATUS_INVERT 0x40 +#define READ_ELEMENT_STATUS_SVALID 0x80 + u_int8_t ssea[2]; /* source storage element address */ + + /* + * bytes 12-47: Primary volume tag information. + * (field omitted if PVOLTAG = 0) + * + * bytes 48-83: Alternate volume tag information. + * (field omitted if AVOLTAG = 0) + * + * bytes 84-87: Reserved (moved up if either of the above fields + * are omitted) + * + * bytes 88-end: Vendor-specific: (moved up if either of the + * above fields are missing) + */ }; -struct scsi_position_to_element -{ - u_char op_code; - u_char :5; - u_char lun:3; - u_char transport_element_address[2]; - u_char source_address[2]; - u_char rsvd[2]; - u_char invert:1; - u_char :7; - u_char link:1; - u_char flag:1; - u_char :6; +/* XXX add data returned by REQUEST VOLUME ELEMENT ADDRESS */ + +/* Element type codes */ +#define ELEMENT_TYPE_MASK 0x0f /* Note: these aren't bits */ +#define ELEMENT_TYPE_ALL 0x00 +#define ELEMENT_TYPE_MT 0x01 +#define ELEMENT_TYPE_ST 0x02 +#define ELEMENT_TYPE_IE 0x03 +#define ELEMENT_TYPE_DT 0x04 + +/* + * XXX The following definitions should be common to all SCSI device types. + */ +#define PGCODE_MASK 0x3f /* valid page number bits in pg_code */ +#define PGCODE_PS 0x80 /* indicates page is savable */ + +/* + * Device capabilities page. + * + * This page defines characteristics of the elemenet types in the + * medium changer device. + * + * Note in the definitions below, the following abbreviations are + * used: + * MT Medium transport element (picker) + * ST Storage transport element (slot) + * IE Import/export element (portal) + * DT Data tranfer element (tape/disk drive) + */ +struct page_device_capabilities { + u_int8_t pg_code; /* page code (0x1f) */ + u_int8_t pg_length; /* page length (0x12) */ + + /* + * The STOR_xx bits indicate that an element of a given + * type may provide independent storage for a unit of + * media. The top four bits of this value are reserved. + */ + u_int8_t stor; +#define STOR_MT 0x01 +#define STOR_ST 0x02 +#define STOR_IE 0x04 +#define STOR_DT 0x08 + + u_int8_t reserved0; + + /* + * The MOVE_TO_yy bits indicate the changer supports + * moving a unit of medium from an element of a given type to an + * element of type yy. This is used to determine if a given + * MOVE MEDIUM command is legal. The top four bits of each + * of these values are reserved. + */ + u_int8_t move_from_mt; + u_int8_t move_from_st; + u_int8_t move_from_ie; + u_int8_t move_from_dt; +#define MOVE_TO_MT 0x01 +#define MOVE_TO_ST 0x02 +#define MOVE_TO_IE 0x04 +#define MOVE_TO_DT 0x08 + + u_int8_t reserved1[2]; + + /* + * Similar to above, but for EXCHANGE MEDIUM. + */ + u_int8_t exchange_with_mt; + u_int8_t exchange_with_st; + u_int8_t exchange_with_ie; + u_int8_t exchange_with_dt; +#define EXCHANGE_WITH_MT 0x01 +#define EXCHANGE_WITH_ST 0x02 +#define EXCHANGE_WITH_IE 0x04 +#define EXCHANGE_WITH_DT 0x08 }; - + /* - * Opcodes + * Medium changer elemement address assignment page. + * + * Some of these fields can be a little confusing, so an explanation + * is in order. + * + * Each component within a a medium changer apparatus is called an + * "element". + * + * The "medium transport element address" is the address of the first + * picker (robotic arm). "Number of medium transport elements" tells + * us how many pickers exist in the changer. + * + * The "first storage element address" is the address of the first + * slot in the tape or disk magazine. "Number of storage elements" tells + * us how many slots exist in the changer. + * + * The "first import/export element address" is the address of the first + * medium portal accessible both by the medium changer and an outside + * human operator. This is where the changer might deposit tapes destined + * for some vault. The "number of import/export elements" tells us + * not many of these portals exist in the changer. NOTE: this number may + * be 0. + * + * The "first data transfer element address" is the address of the first + * tape or disk drive in the changer. "Number of data transfer elements" + * tells us how many drives exist in the changer. */ -#define POSITION_TO_ELEMENT 0x2b -#define MOVE_MEDIUM 0xa5 -#define READ_ELEMENT_STATUS 0xb8 - -struct scsi_element_status_data -{ - u_char first_element_reported[2]; - u_char number_of_elements_reported[2]; - u_char rsvd; - u_char byte_count_of_report[3]; +struct page_element_address_assignment { + u_int8_t pg_code; /* page code (0x1d) */ + u_int8_t pg_length; /* page length (0x12) */ + + /* Medium transport element address */ + u_int8_t mtea[2]; + + /* Number of medium transport elements */ + u_int8_t nmte[2]; + + /* First storage element address */ + u_int8_t fsea[2]; + + /* Number of storage elements */ + u_int8_t nse[2]; + + /* First import/export element address */ + u_int8_t fieea[2]; + + /* Number of import/export elements */ + u_int8_t niee[2]; + + /* First data transfer element address */ + u_int8_t fdtea[2]; + + /* Number of data trafer elements */ + u_int8_t ndte[2]; + + u_int8_t reserved[2]; }; -struct element_status_page -{ - u_char element_type_code; - u_char :5; - u_char avoltag:1; - u_char pvoltag:1; - u_char element_descriptor_length[2]; - u_char rsvd; - u_char byte_count_of_descriptor_data[3]; +/* + * Transport geometry parameters page. + * + * Defines whether each medium transport element is a member of a set of + * elements that share a common robotics subsystem and whether the element + * is capable of media rotation. One transport geometry descriptor is + * transferred for each medium transport element, beginning with the first + * medium transport element (other than the default transport element address + * of 0). + */ +struct page_transport_geometry_parameters { + u_int8_t pg_code; /* page code (0x1e) */ + u_int8_t pg_length; /* page length; variable */ + + /* Transport geometry descriptor(s) are here. */ + + u_int8_t misc; +#define CAN_ROTATE 0x01 + + /* Member number in transport element set. */ + u_int8_t member; }; +#endif /* _SCSI_SCSI_CHANGER_H */ diff --git a/sys/sys/chio.h b/sys/sys/chio.h index 532e6ee..38f3a0a 100644 --- a/sys/sys/chio.h +++ b/sys/sys/chio.h @@ -1,5 +1,7 @@ +/* $Id: $ */ + /* - * Copyright (c) 1982, 1986 The Regents of the University of California. + * Copyright (c) 1996 Jason R. Thorpe <thorpej@and.com> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -11,117 +13,139 @@ * 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 the University of - * California, Berkeley and its contributors. - * 4. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. + * must display the following acknowledgements: + * This product includes software developed by Jason R. Thorpe + * for And Communications, http://www.and.com/ + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 THE REGENTS 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 + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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 _SYS_CHIO_H_ +#define _SYS_CHIO_H_ + +/* + * Element types. Used as "to" and "from" type indicators in move + * and exchange operations. + * + * Note that code in sys/scsi/ch.c relies on these values (uses them + * as offsets in an array, and other evil), so don't muck with them + * unless you know what you're doing. + */ +#define CHET_MT 0 /* medium transport (picker) */ +#define CHET_ST 1 /* storage transport (slot) */ +#define CHET_IE 2 /* import/export (portal) */ +#define CHET_DT 3 /* data transfer (drive) */ + +/* + * Structure used to execute a MOVE MEDIUM command. + */ +struct changer_move { + int cm_fromtype; /* element type to move from */ + int cm_fromunit; /* logical unit of from element */ + int cm_totype; /* element type to move to */ + int cm_tounit; /* logical unit of to element */ + int cm_flags; /* misc. flags */ +}; + +/* cm_flags */ +#define CM_INVERT 0x01 /* invert media */ + +/* + * Structure used to execute an EXCHANGE MEDIUM command. In an + * exchange operation, the following steps occur: * - * @(#)chio.h 7.6 (Berkeley) 2/5/91 + * - media from source is moved to first destination. * - * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE - * -------------------- ----- ---------------------- - * CURRENT PATCH LEVEL: 1 00098 - * -------------------- ----- ---------------------- + * - media previously occupying first destination is moved + * to the second destination. * - * 16 Feb 93 Julian Elischer ADDED for SCSI system + * The second destination may or may not be the same as the source. + * In the case of a simple exchange, the source and second destination + * are the same. */ +struct changer_exchange { + int ce_srctype; /* element type of source */ + int ce_srcunit; /* logical unit of source */ + int ce_fdsttype; /* element type of first destination */ + int ce_fdstunit; /* logical unit of first destination */ + int ce_sdsttype; /* element type of second destination */ + int ce_sdstunit; /* logical unit of second destination */ + int ce_flags; /* misc. flags */ +}; + +/* ce_flags */ +#define CE_INVERT1 0x01 /* invert media 1 */ +#define CE_INVERT2 0x02 /* invert media 2 */ -/* This is a "convertet" mtio.h from 386BSD - Stefan Grefen grefen@goofy.zdv.uni-mainz.de +/* + * Structure used to execute a POSITION TO ELEMENT command. This + * moves the current picker in front of the specified element. */ +struct changer_position { + int cp_type; /* element type */ + int cp_unit; /* logical unit of element */ + int cp_flags; /* misc. flags */ +}; + +/* cp_flags */ +#define CP_INVERT 0x01 /* invert picker */ /* - * Structures and definitions for changer io control commands + * Data returned by CHIOGPARAMS. */ -#ifndef _CHIO_H_ -#define _CHIO_H_ - -#define CH_INVERT 0x10000 -#define CH_ADDR_MASK 0xffff -struct chop { - short ch_op; /* operations defined below */ - short result; /* The result */ - union { - struct { - int chm; /* Transport element */ - int from; - int to; - } move; - struct { - int chm; /* Transport element */ - int to; - } position; - struct { - short chmo; /* Offset of first CHM */ - short chms; /* No. of CHM */ - short slots; /* No. of Storage Elements */ - short sloto; /* Offset of first SE */ - short imexs; /* No. of Import/Export Slots */ - short imexo; /* Offset of first IM/EX */ - short drives; /* No. of CTS */ - short driveo; /* Offset of first CTS */ - short rot; /* CHM can rotate */ - } getparam; - struct { - int type; -#define CH_CHM 1 -#define CH_STOR 2 -#define CH_IMEX 3 -#define CH_CTS 4 - int from; - struct { - u_char elema_1; - u_char elema_0; - u_char full:1; - u_char rsvd:1; - u_char except:1; - u_char :5; - u_char rsvd2; - union { - struct { - u_char add_sense_code; - u_char add_sense_code_qualifier; - } specs; - short add_sense; -/* WARINING LSB only */ -#define CH_CHOLDER 0x0290 /* Cartridge holder is missing */ -#define CH_STATUSQ 0x0390 /* Status is questionable */ -#define CH_CTS_CLOSED 0x0490 /* CTS door is closed */ - - } ch_add_sense; - u_char rsvd3[3]; - u_char :6; - u_char invert:1; - u_char svalid:1; - u_char source_1; - u_char source_0; - u_char rsvd4[4]; - } elem_data; - } get_elem_stat; - } u; +struct changer_params { + int cp_curpicker; /* current picker */ + int cp_npickers; /* number of pickers */ + int cp_nslots; /* number of slots */ + int cp_nportals; /* number of import/export portals */ + int cp_ndrives; /* number of drives */ }; -/* operations */ -#define CHMOVE 1 -#define CHPOSITION 2 -#define CHGETPARAM 3 -#define CHGETELEM 4 +/* + * Command used to get element status. + */ +struct changer_element_status { + int ces_type; /* element type */ + u_int8_t *ces_data; /* pre-allocated data storage */ +}; + +/* + * Data returned by CHIOGSTATUS is an array of flags bytes. + * Not all flags have meaning for all element types. + */ +#define CESTATUS_FULL 0x01 /* element is full */ +#define CESTATUS_IMPEXP 0x02 /* media deposited by operator */ +#define CESTATUS_EXCEPT 0x04 /* element in abnormal state */ +#define CESTATUS_ACCESS 0x08 /* media accessible by picker */ +#define CESTATUS_EXENAB 0x10 /* element supports exporting */ +#define CESTATUS_INENAB 0x20 /* element supports importing */ + +#define CESTATUS_PICKER_MASK 0x05 /* flags valid for pickers */ +#define CESTATUS_SLOT_MASK 0x0c /* flags valid for slots */ +#define CESTATUS_PORTAL_MASK 0x3f /* flags valid for portals */ +#define CESTATUS_DRIVE_MASK 0x0c /* flags valid for drives */ + +#define CESTATUS_BITS \ + "\20\6INEAB\5EXENAB\4ACCESS\3EXCEPT\2IMPEXP\1FULL" +#define CHIOMOVE _IOW('c', 0x01, struct changer_move) +#define CHIOEXCHANGE _IOW('c', 0x02, struct changer_exchange) +#define CHIOPOSITION _IOW('c', 0x03, struct changer_position) +#define CHIOGPICKER _IOR('c', 0x04, int) +#define CHIOSPICKER _IOW('c', 0x05, int) +#define CHIOGPARAMS _IOR('c', 0x06, struct changer_params) +#define CHIOGSTATUS _IOW('c', 0x08, struct changer_element_status) -/* Changer IO control command */ -#define CHIOOP _IOWR('c', 1, struct chop) /* do a mag tape op */ -#endif +#endif /* _SYS_CHIO_H_ */ |