diff options
Diffstat (limited to 'bin/chio')
-rw-r--r-- | bin/chio/Makefile | 6 | ||||
-rw-r--r-- | bin/chio/Makefile.depend | 18 | ||||
-rw-r--r-- | bin/chio/chio.1 | 300 | ||||
-rw-r--r-- | bin/chio/chio.c | 1249 | ||||
-rw-r--r-- | bin/chio/defs.h | 57 | ||||
-rw-r--r-- | bin/chio/pathnames.h | 35 |
6 files changed, 1665 insertions, 0 deletions
diff --git a/bin/chio/Makefile b/bin/chio/Makefile new file mode 100644 index 0000000..5157f65 --- /dev/null +++ b/bin/chio/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= chio + +.include <bsd.prog.mk> diff --git a/bin/chio/Makefile.depend b/bin/chio/Makefile.depend new file mode 100644 index 0000000..3646e2e --- /dev/null +++ b/bin/chio/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/bin/chio/chio.1 b/bin/chio/chio.1 new file mode 100644 index 0000000..a61242b --- /dev/null +++ b/bin/chio/chio.1 @@ -0,0 +1,300 @@ +.\" $NetBSD: chio.1,v 1.4 1997/10/02 00:41:25 hubertf Exp $ +.\"- +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd May 14, 1998 +.Dt CHIO 1 +.Os +.Sh NAME +.Nm chio +.Nd medium changer control utility +.Sh SYNOPSIS +.Nm +.Op Fl f Ar changer +.Ar command +.Op Fl <flags> +.Ar arg1 +.Ar arg2 +.Op Ar arg3 Op ... +.Sh DESCRIPTION +The +.Nm +utility 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 +.Ar changer +rather than the default device +.Pa /dev/ch0 . +.El +.Pp +The default changer may be overridden by setting the environment variable +.Ev CHANGER +to the desired changer device. +.Pp +A medium changer apparatus is made up of +.Em elements . +There are five element types: +.Em picker +(medium transport), +.Em slot +(storage), +.Em portal +(import/export), +.Em drive +(data transfer), and +.Em voltag +(select by volume identifier). +The +.Em voltag +pseudo-element type allows the selection of tapes by their volume tag +(typically a barcode on the tape). +.Pp +In this command description, the shorthand +.Em ET +will be used to represent an element type, and +.Em EU +will be used to represent an element unit. +For example, to represent the first robotic arm in the changer, the +.Em ET +would be +.Dq picker +and the +.Em EU +would be +.Dq 0 . +.Sh SUPPORTED COMMANDS +.Bl -tag -width indent +.It Ic move Xo +.Ar <from ET> <from EU> <to ET> <to EU> +.Op Cm inv +.Xc +Move the media unit from +.Ar <from ET/EU> +to +.Ar <to ET/EU> . +If the optional modifier +.Cm inv +is specified, the media unit will be inverted before insertion. +.It Ic exchange Xo +.Ar <src ET> <src EU> <dst1 ET> <dst1 EU> +.Op Ar <dst2 ET> <dst2 ET> +.Op Cm inv1 +.Op Cm inv2 +.Xc +Perform a media unit exchange operation. +The media unit in +.Ar <src ET/EU> +is moved to +.Ar <dst1 ET/EU> +and the media unit previously in +.Ar <dst1 ET/EU> +is moved to +.Ar <dst2 ET/EU> . +In the case of a simple exchange, +.Ar <dst2 ET/EU> +is omitted and the values +.Ar <src ET/EU> +are used in their place. +The optional modifiers +.Cm inv1 +and +.Cm inv2 +specify whether the media units are to be inverted before insertion into +.Ar <dst1 ET/EU> +and +.Ar <dst2 ET/EU> +respectively. +.Pp +Note that not all medium changers support the +.Ic exchange +operation; the changer must have multiple free pickers or emulate +multiple free pickers with transient storage. +.It Ic return Xo +.Ar <from ET> <from EU> +.Xc +Return the media unit to its source element. +This command will query the status of the specified media unit, and +will move it to the element specified in its source attribute. +This is a convenient way to return media from a drive or portal +to its previous element in the changer. +.It Ic position Xo +.Ar <to ET> <to EU> +.Op Cm inv +.Xc +Position the picker in front of the element described by +.Ar <to ET/EU> . +If the optional modifier +.Cm inv +is specified, the media unit will be inverted before insertion. +.Pp +Note that not all changers behave as expected when issued this command. +.It Ic params +Report the number of slots, drives, pickers, and portals in the changer, +and which picker unit the changer is currently configured to use. +.It Ic getpicker +Report which picker unit the changer is currently configured to use. +.It Ic setpicker Xo +.Ar <unit> +.Xc +Configure the changer to use picker +.Ar <unit> . +.It Ic ielem Xo +.Op Ar <timeout> +.Xc +Perform an +.Em INITIALIZE ELEMENT STATUS +operation on the changer. +The optional +.Ar <timeout> +parameter may be given to specify a timeout in seconds for the +operations. +This may be used if the operation takes unusually long +because of buggy firmware or the like. +.It Ic voltag Xo +.Op Fl fca +.Ar <ET> +.Ar <EU> +.Op Ar <label> +.Op Ar <serial> +.Xc +Change volume tag for an element in the media changer. +This command +is only supported by few media changers. +If it is not supported by a +device, using this command will usually result in an "Invalid Field in +CDB" error message on the console. +.Pp +If the +.Fl c +flag is specified, the volume tag of the specified element is +cleared. +If the +.Fl f +flag is specified, the volume tag is superseded with the specified +volume tag even if a volume tag is already defined for the element. +It is an error to not specify the +.Fl f +flag when trying to set a label for an element which already has +volume tag information defined. +.Pp +The command works with the primary volume tag or, if the +.Fl a +flag is given, with the alternate volume tag. +.It Ic status Xo +.Op Fl vVsSbIa +.Op Ar <type> +.Xc +Report the status of all elements in the changer. +If +.Ar <type> +is specified, report the status of all elements of type +.Ar <type> . +.It Fl v +Print the primary volume tag for each loaded medium, if any. +The volume +tag is printed as +.Dq <LABEL:SERIAL> . +.It Fl V +Print the alternate volume tag for each loaded medium, if any. +.It Fl s +Print the additional sense code and additional sense code qualifier for +each element. +.It Fl S +Print the element source address for each element. +.It Fl b +Print SCSI bus information for each element. +Note that this information +is valid only for drives. +.It Fl I +Print the internal element addresses for each element. +The internal +element address is not normally used with this driver. +It is reported +for diagnostic purposes only. +.It Fl a +Print all additional information (as in +.Fl vVsSba ) . +.El +.Pp +The status bits are defined as follows: +.Bl -tag -width indent +.It FULL +Element contains a media unit. +.It IMPEXP +Media was deposited into element by an outside human operator. +.It EXCEPT +Element is in an abnormal state. +.It ACCESS +Media in this element is accessible by a picker. +.It EXENAB +Element supports passing media (exporting) to an outside human operator. +.It INENAB +Element supports receiving media (importing) from an outside human operator. +.El +.Sh FILES +.Bl -tag -width /dev/ch0 -compact +.It Pa /dev/ch0 +default changer device +.El +.Sh EXAMPLES +.Bl -tag -width indent +.It Li chio move slot 3 drive 0 +Move the media in slot 3 (fourth slot) to drive 0 (first drive). +.It Li chio move voltag VOLUME01 drive 0 +Move the media with the barcode VOLUME01 to drive 0 (first drive). +.It Li chio return drive 0 +Remove the tape from drive 0 (first drive) and return it to its original +location in the rack. +.It Li chio setpicker 2 +Configure the changer to use picker 2 (third picker) for operations. +.El +.Sh SEE ALSO +.Xr mt 1 , +.Xr mount 8 +.Sh AUTHORS +.An -nosplit +The +.Nm +program and SCSI changer driver were written by +.An Jason R. Thorpe Aq Mt thorpej@and.com +for And Communications, +.Pa http://www.and.com/ . +.Pp +Additional work by +.An Hans Huebner Aq Mt hans@artcom.de +and +.An Steve Gunn Aq Mt csg@waterspout.com . diff --git a/bin/chio/chio.c b/bin/chio/chio.c new file mode 100644 index 0000000..5a2a7ba --- /dev/null +++ b/bin/chio/chio.c @@ -0,0 +1,1249 @@ +/* $NetBSD: chio.c,v 1.6 1998/01/04 23:53:58 thorpej Exp $ */ +/*- + * 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. + */ +/* + * Additional Copyright (c) 1997, by Matthew Jacob, for NASA/Ames Research Ctr. + * Addidional Copyright (c) 2000, by C. Stephen Gunn, Waterspout Communications + */ + +#if 0 +#ifndef lint +static const char copyright[] = + "@(#) Copyright (c) 1996 Jason R. Thorpe. All rights reserved."; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/chio.h> +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <langinfo.h> +#include <locale.h> + +#include "defs.h" +#include "pathnames.h" + +static void usage(void); +static void cleanup(void); +static u_int16_t parse_element_type(char *); +static u_int16_t parse_element_unit(char *); +static const char * element_type_name(int et); +static int parse_special(char *); +static int is_special(char *); +static const char *bits_to_string(ces_status_flags, const char *); + +static void find_element(char *, uint16_t *, uint16_t *); +static struct changer_element_status *get_element_status + (unsigned int, unsigned int, int); + +static int do_move(const char *, int, char **); +static int do_exchange(const char *, int, char **); +static int do_position(const char *, int, char **); +static int do_params(const char *, int, char **); +static int do_getpicker(const char *, int, char **); +static int do_setpicker(const char *, int, char **); +static int do_status(const char *, int, char **); +static int do_ielem(const char *, int, char **); +static int do_return(const char *, int, char **); +static int do_voltag(const char *, int, char **); +static void print_designator(const char *, u_int8_t, u_int8_t); + +#ifndef CHET_VT +#define CHET_VT 10 /* Completely Arbitrary */ +#endif + +/* Valid changer element types. */ +static const struct element_type elements[] = { + { "drive", CHET_DT }, + { "picker", CHET_MT }, + { "portal", CHET_IE }, + { "slot", CHET_ST }, + { "voltag", CHET_VT }, /* Select tapes by barcode */ + { NULL, 0 }, +}; + +/* Valid commands. */ +static const struct changer_command commands[] = { + { "exchange", do_exchange }, + { "getpicker", do_getpicker }, + { "ielem", do_ielem }, + { "move", do_move }, + { "params", do_params }, + { "position", do_position }, + { "setpicker", do_setpicker }, + { "status", do_status }, + { "return", do_return }, + { "voltag", do_voltag }, + { NULL, 0 }, +}; + +/* Valid special words. */ +static const struct special_word specials[] = { + { "inv", SW_INVERT }, + { "inv1", SW_INVERT1 }, + { "inv2", SW_INVERT2 }, + { NULL, 0 }, +}; + +static int changer_fd; +static const char *changer_name; + +int +main(int argc, char **argv) +{ + int ch, i; + + 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) { + /* look for abbreviation */ + for (i = 0; commands[i].cc_name != NULL; ++i) + if (strncmp(*argv, commands[i].cc_name, + strlen(*argv)) == 0) + break; + } + + if (commands[i].cc_name == NULL) + errx(1, "unknown command: %s", *argv); + + exit ((*commands[i].cc_handler)(commands[i].cc_name, argc, argv)); + /* NOTREACHED */ +} + +static int +do_move(const 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. + */ + + ++argv; --argc; + + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 5) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, sizeof(cmd)); + + /* <from ET> */ + cmd.cm_fromtype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.cm_fromtype) { + find_element(*argv, &cmd.cm_fromtype, &cmd.cm_fromunit); + } else { + /* <from EU> */ + cmd.cm_fromunit = parse_element_unit(*argv); + } + ++argv; --argc; + + /* <to ET> */ + cmd.cm_totype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type, and report error */ + if (CHET_VT == cmd.cm_totype) + errx(1,"%s: voltag only makes sense as an element source", + cname); + + /* <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, &cmd)) + err(1, "%s: CHIOMOVE", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s " + "<from ET> <from EU> <to ET> <to EU> [inv]\n", getprogname(), cname); + return (1); +} + +static int +do_exchange(const 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. + */ + + ++argv; --argc; + + if (argc < 4) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 8) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, sizeof(cmd)); + + /* <src ET> */ + cmd.ce_srctype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.ce_srctype) { + find_element(*argv, &cmd.ce_srctype, &cmd.ce_srcunit); + } else { + /* <from EU> */ + cmd.ce_srcunit = parse_element_unit(*argv); + } + ++argv; --argc; + + /* <dst1 ET> */ + cmd.ce_fdsttype = parse_element_type(*argv); + ++argv; --argc; + + /* Check for voltag virtual type */ + if (CHET_VT == cmd.ce_fdsttype) { + find_element(*argv, &cmd.ce_fdsttype, &cmd.ce_fdstunit); + } else { + /* <from 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; + + if (CHET_VT == cmd.ce_sdsttype) + errx(1,"%s %s: voltag only makes sense as an element source", + cname, *argv); + + /* <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, &cmd)) + err(1, "%s: CHIOEXCHANGE", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, + "usage: %s %s <src ET> <src EU> <dst1 ET> <dst1 EU>\n" + " [<dst2 ET> <dst2 EU>] [inv1] [inv2]\n", + getprogname(), cname); + return (1); +} + +static int +do_position(const 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. + */ + + ++argv; --argc; + + if (argc < 2) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + (void) memset(&cmd, 0, 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, &cmd)) + err(1, "%s: CHIOPOSITION", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s <to ET> <to EU> [inv]\n", + getprogname(), cname); + return (1); +} + +/* ARGSUSED */ +static int +do_params(const char *cname, int argc, char **argv) +{ + struct changer_params data; + int picker; + + /* No arguments to this command. */ + + ++argv; --argc; + + if (argc) { + warnx("%s: no arguments expected", cname); + goto usage; + } + + /* Get params from changer and display them. */ + (void) memset(&data, 0, sizeof(data)); + if (ioctl(changer_fd, CHIOGPARAMS, &data)) + err(1, "%s: CHIOGPARAMS", changer_name); + + (void) 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) + (void) printf(", %d portal%s", data.cp_nportals, + (data.cp_nportals > 1) ? "s" : ""); + + /* Get current picker from changer and display it. */ + if (ioctl(changer_fd, CHIOGPICKER, &picker)) + err(1, "%s: CHIOGPICKER", changer_name); + + (void) printf("\n%s: current picker: %d\n", changer_name, picker); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname); + return (1); +} + +/* ARGSUSED */ +static int +do_getpicker(const char *cname, int argc, char **argv) +{ + int picker; + + /* No arguments to this command. */ + + ++argv; --argc; + + if (argc) { + warnx("%s: no arguments expected", cname); + goto usage; + } + + /* Get current picker from changer and display it. */ + if (ioctl(changer_fd, CHIOGPICKER, &picker)) + err(1, "%s: CHIOGPICKER", changer_name); + + (void) printf("%s: current picker: %d\n", changer_name, picker); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s\n", getprogname(), cname); + return (1); +} + +static int +do_setpicker(const char *cname, int argc, char **argv) +{ + int picker; + + ++argv; --argc; + + 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, &picker)) + err(1, "%s: CHIOSPICKER", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s <picker>\n", getprogname(), cname); + return (1); +} + +static int +do_status(const char *cname, int argc, char **argv) +{ + struct changer_params cp; + struct changer_element_status_request cesr; + int i; + u_int16_t base, count, chet, schet, echet; + const char *description; + int pvoltag = 0; + int avoltag = 0; + int sense = 0; + int scsi = 0; + int source = 0; + int intaddr = 0; + int c; + + count = 0; + base = 0; + description = NULL; + + optind = optreset = 1; + while ((c = getopt(argc, argv, "vVsSbaI")) != -1) { + switch (c) { + case 'v': + pvoltag = 1; + break; + case 'V': + avoltag = 1; + break; + case 's': + sense = 1; + break; + case 'S': + source = 1; + break; + case 'b': + scsi = 1; + break; + case 'I': + intaddr = 1; + break; + case 'a': + pvoltag = avoltag = source = sense = scsi = intaddr = 1; + break; + default: + warnx("%s: bad option", cname); + goto usage; + } + } + + argc -= optind; + argv += optind; + + /* + * On a status command, we expect the following: + * + * [<ET> [<start> [<end>] ] ] + * + * where ET == element type, start == first element to report, + * end == number of elements to report + * + * If we get no arguments, we get the status of all + * known element types. + */ + if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + + /* + * Get params from changer. Specifically, we need the element + * counts. + */ + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) + err(1, "%s: CHIOGPARAMS", changer_name); + + if (argc > 0) + schet = echet = parse_element_type(argv[0]); + else { + schet = CHET_MT; + echet = CHET_DT; + } + if (argc > 1) { + base = (u_int16_t)atol(argv[1]); + count = 1; + } + if (argc > 2) + count = (u_int16_t)atol(argv[2]) - base + 1; + + for (chet = schet; chet <= echet; ++chet) { + switch (chet) { + case CHET_MT: + if (count == 0) + count = cp.cp_npickers; + else if (count > cp.cp_npickers) + errx(1, "not that many pickers in device"); + description = "picker"; + break; + + case CHET_ST: + if (count == 0) + count = cp.cp_nslots; + else if (count > cp.cp_nslots) + errx(1, "not that many slots in device"); + description = "slot"; + break; + + case CHET_IE: + if (count == 0) + count = cp.cp_nportals; + else if (count > cp.cp_nportals) + errx(1, "not that many portals in device"); + description = "portal"; + break; + + case CHET_DT: + if (count == 0) + count = cp.cp_ndrives; + else if (count > cp.cp_ndrives) + errx(1, "not that many drives in device"); + description = "drive"; + break; + + default: + /* To appease gcc -Wuninitialized. */ + count = 0; + description = NULL; + } + + if (count == 0) { + if (argc == 0) + continue; + else { + printf("%s: no %s elements\n", + changer_name, description); + return (0); + } + } + + bzero(&cesr, sizeof(cesr)); + cesr.cesr_element_type = chet; + cesr.cesr_element_base = base; + cesr.cesr_element_count = count; + /* Allocate storage for the status structures. */ + cesr.cesr_element_status = + (struct changer_element_status *) + calloc((size_t)count, sizeof(struct changer_element_status)); + + if (!cesr.cesr_element_status) + errx(1, "can't allocate status storage"); + + if (avoltag || pvoltag) + cesr.cesr_flags |= CESR_VOLTAGS; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr)) { + free(cesr.cesr_element_status); + err(1, "%s: CHIOGSTATUS", changer_name); + } + + /* Dump the status for each reported element. */ + for (i = 0; i < count; ++i) { + struct changer_element_status *ces = + &(cesr.cesr_element_status[i]); + printf("%s %d: %s", description, ces->ces_addr, + bits_to_string(ces->ces_flags, + CESTATUS_BITS)); + if (sense) + printf(" sense: <0x%02x/0x%02x>", + ces->ces_sensecode, + ces->ces_sensequal); + if (pvoltag) + printf(" voltag: <%s:%d>", + ces->ces_pvoltag.cv_volid, + ces->ces_pvoltag.cv_serial); + if (avoltag) + printf(" avoltag: <%s:%d>", + ces->ces_avoltag.cv_volid, + ces->ces_avoltag.cv_serial); + if (source) { + if (ces->ces_flags & CES_SOURCE_VALID) + printf(" source: <%s %d>", + element_type_name( + ces->ces_source_type), + ces->ces_source_addr); + else + printf(" source: <>"); + } + if (intaddr) + printf(" intaddr: <%d>", ces->ces_int_addr); + if (scsi) { + printf(" scsi: <"); + if (ces->ces_flags & CES_SCSIID_VALID) + printf("%d", ces->ces_scsi_id); + else + putchar('?'); + putchar(':'); + if (ces->ces_flags & CES_LUN_VALID) + printf("%d", ces->ces_scsi_lun); + else + putchar('?'); + putchar('>'); + } + if (ces->ces_designator_length > 0) + print_designator(ces->ces_designator, + ces->ces_code_set, + ces->ces_designator_length); + putchar('\n'); + } + + free(cesr.cesr_element_status); + count = 0; + } + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s [-vVsSbaA] [<element type> [<start-addr> [<end-addr>] ] ]\n", + getprogname(), cname); + return (1); +} + +static int +do_ielem(const char *cname, int argc, char **argv) +{ + int timeout = 0; + + if (argc == 2) { + timeout = atol(argv[1]); + } else if (argc > 1) { + warnx("%s: too many arguments", cname); + goto usage; + } + + if (ioctl(changer_fd, CHIOIELEM, &timeout)) + err(1, "%s: CHIOIELEM", changer_name); + + return (0); + + usage: + (void) fprintf(stderr, "usage: %s %s [<timeout>]\n", + getprogname(), cname); + return (1); +} + +static int +do_voltag(const char *cname, int argc, char **argv) +{ + int force = 0; + int clear = 0; + int alternate = 0; + int c; + struct changer_set_voltag_request csvr; + + bzero(&csvr, sizeof(csvr)); + + optind = optreset = 1; + while ((c = getopt(argc, argv, "fca")) != -1) { + switch (c) { + case 'f': + force = 1; + break; + case 'c': + clear = 1; + break; + case 'a': + alternate = 1; + break; + default: + warnx("%s: bad option", cname); + goto usage; + } + } + + argc -= optind; + argv += optind; + + if (argc < 2) { + warnx("%s: missing element specification", cname); + goto usage; + } + + csvr.csvr_type = parse_element_type(argv[0]); + csvr.csvr_addr = (u_int16_t)atol(argv[1]); + + if (!clear) { + if (argc < 3 || argc > 4) { + warnx("%s: missing argument", cname); + goto usage; + } + + if (force) + csvr.csvr_flags = CSVR_MODE_REPLACE; + else + csvr.csvr_flags = CSVR_MODE_SET; + + if (strlen(argv[2]) > sizeof(csvr.csvr_voltag.cv_volid)) { + warnx("%s: volume label too long", cname); + goto usage; + } + + strlcpy((char *)csvr.csvr_voltag.cv_volid, argv[2], + sizeof(csvr.csvr_voltag.cv_volid)); + + if (argc == 4) { + csvr.csvr_voltag.cv_serial = (u_int16_t)atol(argv[3]); + } + } else { + if (argc != 2) { + warnx("%s: unexpected argument", cname); + goto usage; + } + csvr.csvr_flags = CSVR_MODE_CLEAR; + } + + if (alternate) { + csvr.csvr_flags |= CSVR_ALTERNATE; + } + + if (ioctl(changer_fd, CHIOSETVOLTAG, &csvr)) + err(1, "%s: CHIOSETVOLTAG", changer_name); + + return 0; + usage: + (void) fprintf(stderr, + "usage: %s %s [-fca] <element> [<voltag> [<vsn>] ]\n", + getprogname(), cname); + return 1; +} + +static u_int16_t +parse_element_type(char *cp) +{ + int i; + + for (i = 0; elements[i].et_name != NULL; ++i) + if (strcmp(elements[i].et_name, cp) == 0) + return ((u_int16_t)elements[i].et_type); + + errx(1, "invalid element type `%s'", cp); + /* NOTREACHED */ +} + +static const char * +element_type_name(int et) +{ + int i; + + for (i = 0; elements[i].et_name != NULL; i++) + if (elements[i].et_type == et) + return elements[i].et_name; + + return "unknown"; +} + +static u_int16_t +parse_element_unit(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 ((u_int16_t)i); +} + +static int +parse_special(char *cp) +{ + int val; + + val = is_special(cp); + if (val) + return (val); + + errx(1, "invalid modifier `%s'", cp); + /* NOTREACHED */ +} + +static int +is_special(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 const char * +bits_to_string(ces_status_flags v, const char *cp) +{ + const char *np; + char f, sep, *bp; + static char buf[128]; + + bp = buf; + (void) memset(buf, 0, sizeof(buf)); + + for (sep = '<'; (f = *cp++) != 0; cp = np) { + for (np = cp; *np >= ' ';) + np++; + if (((int)v & (1 << (f - 1))) == 0) + continue; + (void) snprintf(bp, sizeof(buf) - (size_t)(bp - &buf[0]), + "%c%.*s", sep, (int)(long)(np - cp), cp); + bp += strlen(bp); + sep = ','; + } + if (sep != '<') + *bp = '>'; + + return (buf); +} +/* + * do_return() + * + * Given an element reference, ask the changer/picker to move that + * element back to its source slot. + */ +static int +do_return(const char *cname, int argc, char **argv) +{ + struct changer_element_status *ces; + struct changer_move cmd; + uint16_t type, element; + + ++argv; --argc; + + if (argc < 2) { + warnx("%s: too few arguments", cname); + goto usage; + } else if (argc > 3) { + warnx("%s: too many arguments", cname); + goto usage; + } + + type = parse_element_type(*argv); + ++argv; --argc; + + /* Handle voltag virtual Changer Element Type */ + if (CHET_VT == type) { + find_element(*argv, &type, &element); + } else { + element = parse_element_unit(*argv); + } + ++argv; --argc; + + /* Get the status */ + ces = get_element_status((unsigned int)type, (unsigned int)element, + CHET_VT == type); + + if (NULL == ces) + errx(1, "%s: null element status pointer", cname); + + if (!(ces->ces_flags & CES_SOURCE_VALID)) + errx(1, "%s: no source information", cname); + + (void) memset(&cmd, 0, sizeof(cmd)); + + cmd.cm_fromtype = type; + cmd.cm_fromunit = element; + cmd.cm_totype = ces->ces_source_type; + cmd.cm_tounit = ces->ces_source_addr; + + if (ioctl(changer_fd, CHIOMOVE, &cmd) == -1) + err(1, "%s: CHIOMOVE", changer_name); + free(ces); + + return(0); + +usage: + (void) fprintf(stderr, "usage: %s %s " + "<from ET> <from EU>\n", getprogname(), cname); + return(1); +} + +/* + * get_element_status() + * + * return a *cesr for the specified changer element. This + * routing will malloc()/calloc() the memory. The caller + * should free() it when done. + */ +static struct changer_element_status * +get_element_status(unsigned int type, unsigned int element, int use_voltags) +{ + struct changer_element_status_request cesr; + struct changer_element_status *ces; + + ces = (struct changer_element_status *) + calloc((size_t)1, sizeof(struct changer_element_status)); + + if (NULL == ces) + errx(1, "can't allocate status storage"); + + (void)memset(&cesr, 0, sizeof(cesr)); + + cesr.cesr_element_type = (uint16_t)type; + cesr.cesr_element_base = (uint16_t)element; + cesr.cesr_element_count = 1; /* Only this one element */ + if (use_voltags) + cesr.cesr_flags |= CESR_VOLTAGS; /* Grab voltags as well */ + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ces); + err(1, "%s: CHIOGSTATUS", changer_name); + /* NOTREACHED */ + } + + return ces; +} + + +/* + * find_element() + * + * Given a <voltag> find the chager element and unit, or exit + * with an error if it isn't found. We grab the changer status + * and iterate until we find a match, or crap out. + */ +static void +find_element(char *voltag, uint16_t *et, uint16_t *eu) +{ + struct changer_params cp; + struct changer_element_status_request cesr; + struct changer_element_status *ch_ces, *ces; + int found = 0; + size_t elem, total_elem; + + /* + * Get the changer parameters, we're interested in the counts + * for all types of elements to perform our search. + */ + if (ioctl(changer_fd, CHIOGPARAMS, (char *)&cp)) + err(1, "%s: CHIOGPARAMS", changer_name); + + /* Allocate some memory for the results */ + total_elem = (cp.cp_nslots + cp.cp_ndrives + + cp.cp_npickers + cp.cp_nportals); + + ch_ces = (struct changer_element_status *) + calloc(total_elem, sizeof(struct changer_element_status)); + + if (NULL == ch_ces) + errx(1, "can't allocate status storage"); + + ces = ch_ces; + + /* Read in the changer slots */ + if (cp.cp_nslots > 0) { + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_ST; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_nslots; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_nslots; + } + + /* Read in the drive information */ + if (cp.cp_ndrives > 0 ) { + + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_DT; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_ndrives; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_ndrives; + } + + /* Read in the portal information */ + if (cp.cp_nportals > 0 ) { + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_IE; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_nportals; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + ces += cp.cp_nportals; + } + + /* Read in the picker information */ + if (cp.cp_npickers > 0) { + (void) memset(&cesr, 0, sizeof(cesr)); + cesr.cesr_element_type = CHET_MT; + cesr.cesr_element_base = 0; + cesr.cesr_element_count = cp.cp_npickers; + cesr.cesr_flags |= CESR_VOLTAGS; + cesr.cesr_element_status = ces; + + if (ioctl(changer_fd, CHIOGSTATUS, (char *)&cesr) == -1) { + free(ch_ces); + err(1, "%s: CHIOGSTATUS", changer_name); + } + } + + /* + * Now search the list the specified <voltag> + */ + for (elem = 0; elem <= total_elem; ++elem) { + + ces = &ch_ces[elem]; + + /* Make sure we have a tape in this element */ + if ((ces->ces_flags & (CES_STATUS_ACCESS|CES_STATUS_FULL)) + != (CES_STATUS_ACCESS|CES_STATUS_FULL)) + continue; + + /* Check to see if it is our target */ + if (strcasecmp(voltag, + (const char *)ces->ces_pvoltag.cv_volid) == 0) { + *et = ces->ces_type; + *eu = ces->ces_addr; + ++found; + break; + } + } + if (!found) { + errx(1, "%s: unable to locate voltag: %s", changer_name, + voltag); + } + free(ch_ces); + return; +} + +static void +cleanup(void) +{ + /* Simple enough... */ + (void)close(changer_fd); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: %s [-f changer] command [-<flags>] " + "arg1 arg2 [arg3 [...]]\n", getprogname()); + exit(1); +} + +#define UTF8CODESET "UTF-8" + +static void +print_designator(const char *designator, u_int8_t code_set, + u_int8_t designator_length) +{ + printf(" serial number: <"); + switch (code_set) { + case CES_CODE_SET_ASCII: { + /* + * The driver insures that the string is always NUL terminated. + */ + printf("%s", designator); + break; + } + case CES_CODE_SET_UTF_8: { + char *cs_native; + + setlocale(LC_ALL, ""); + cs_native = nl_langinfo(CODESET); + + /* See if we can natively print UTF-8 */ + if (strcmp(cs_native, UTF8CODESET) == 0) + cs_native = NULL; + + if (cs_native == NULL) { + /* We can natively print UTF-8, so use printf. */ + printf("%s", designator); + } else { + int i; + + /* + * We can't natively print UTF-8. We should + * convert it to the terminal's codeset, but that + * requires iconv(3) and FreeBSD doesn't have + * iconv(3) in the base system yet. So we use %XX + * notation for non US-ASCII characters instead. + */ + for (i = 0; i < designator_length && + designator[i] != '\0'; i++) { + if ((unsigned char)designator[i] < 0x80) + printf("%c", designator[i]); + else + printf("%%%02x", + (unsigned char)designator[i]); + } + } + break; + } + case CES_CODE_SET_BINARY: { + int i; + + for (i = 0; i < designator_length; i++) + printf("%02X%s", designator[i], + (i == designator_length - 1) ? "" : " "); + break; + } + default: + break; + } + printf(">"); +} diff --git a/bin/chio/defs.h b/bin/chio/defs.h new file mode 100644 index 0000000..8f10c9d --- /dev/null +++ b/bin/chio/defs.h @@ -0,0 +1,57 @@ +/* $FreeBSD$ */ + +/*- + * 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 { + const char *et_name; /* name; i.e. "picker, "slot", etc. */ + int et_type; /* type number */ +}; + +struct changer_command { + const char *cc_name; /* command name */ + /* command handler */ + int (*cc_handler)(const char *, int, char **); +}; + +struct special_word { + const 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..5b9b1e4 --- /dev/null +++ b/bin/chio/pathnames.h @@ -0,0 +1,35 @@ +/* $FreeBSD$ */ + +/*- + * 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" |