Return to BSD News archive
Newsgroups: comp.unix.bsd
Path: sserve!manuel!munnari.oz.au!network.ucsd.edu!swrinde!cs.utexas.edu!sun-barr!ames!agate!tfs.com!tfs.com!julian
From: julian@tfs.com (Julian Elischer)
Subject: New scsi system beta3 (part 5 of 10)
Message-ID: <1992Oct3.040155.13763@tfs.com>
Organization: TRW Financial Systems
Date: Sat, 3 Oct 1992 04:01:55 GMT
Lines: 1315
# This is a shell archive. Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file". Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
# sys/chio.h
# scsi/scsi_changer.h
# scsi/ch.c
#
echo x - sys/chio.h
sed 's/^X//' >sys/chio.h << 'END-of-sys/chio.h'
X/*
X * Copyright (c) 1982, 1986 The Regents of the University of California.
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X * notice, this list of conditions and the following disclaimer in the
X * documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X * must display the following acknowledgement:
X * This product includes software developed by the University of
X * California, Berkeley and its contributors.
X * 4. Neither the name of the University nor the names of its contributors
X * may be used to endorse or promote products derived from this software
X * without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
X * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X *
X * @(#)chio.h 7.6 (Berkeley) 2/5/91
X */
X
X/* This is a "convertet" mtio.h from 386BSD
X Stefan Grefen grefen@goofy.zdv.uni-mainz.de
X */
X
X/*
X * Structures and definitions for changer io control commands
X */
X#ifndef _CHIO_H_
X#define _CHIO_H_
X
X#define CH_INVERT 0x10000
X#define CH_ADDR_MASK 0xffff
Xstruct chop {
X short ch_op; /* operations defined below */
X short result; /* The result */
X union {
X struct {
X int chm; /* Transport element */
X int from;
X int to;
X } move;
X struct {
X int chm; /* Transport element */
X int to;
X } position;
X struct {
X short chmo; /* Offset of first CHM */
X short chms; /* No. of CHM */
X short slots; /* No. of Storage Elements */
X short sloto; /* Offset of first SE */
X short imexs; /* No. of Import/Export Slots */
X short imexo; /* Offset of first IM/EX */
X short drives; /* No. of CTS */
X short driveo; /* Offset of first CTS */
X short rot; /* CHM can rotate */
X } getparam;
X struct {
X int type;
X#define CH_CHM 1
X#define CH_STOR 2
X#define CH_IMEX 3
X#define CH_CTS 4
X int from;
X struct {
X u_char elema_1;
X u_char elema_0;
X u_char full:1;
X u_char rsvd:1;
X u_char except:1;
X u_char :5;
X u_char rsvd2;
X union {
X struct {
X u_char add_sense_code;
X u_char add_sense_code_qualifier;
X } specs;
X short add_sense;
X/* WARINING LSB only */
X#define CH_CHOLDER 0x0290 /* Cartridge holder is missing */
X#define CH_STATUSQ 0x0390 /* Status is questionable */
X#define CH_CTS_CLOSED 0x0490 /* CTS door is closed */
X
X } ch_add_sense;
X u_char rsvd3[3];
X u_char :6;
X u_char invert:1;
X u_char svalid:1;
X u_char source_1;
X u_char source_0;
X u_char rsvd4[4];
X } elem_data;
X } get_elem_stat;
X } u;
X};
X
X/* operations */
X#define CHMOVE 1
X#define CHPOSITION 2
X#define CHGETPARAM 3
X#define CHGETELEM 4
X
X
X/* Changer IO control command */
X#define CHIOOP _IOWR('c', 1, struct chop) /* do a mag tape op */
X#endif
END-of-sys/chio.h
echo x - scsi/scsi_changer.h
sed 's/^X//' >scsi/scsi_changer.h << 'END-of-scsi/scsi_changer.h'
X/*
X * HISTORY
X * $Log: scsi_tape.h,v $
X *
X */
X
X/*
X * SCSI changer interface description
X */
X
X/*
X * Written by Stefan Grefen (grefen@goofy.zdv.uni-mainz.de soon grefen@convex.com)
X * based on the SCSI System by written Julian Elischer (julian@tfs.com)
X * for TRW Financial Systems.
X *
X * TRW Financial Systems, in accordance with their agreement with Carnegie
X * Mellon University, makes this software available to CMU to distribute
X * or use in any manner that they see fit as long as this message is kept with
X * the software. For this reason TFS also grants any other persons or
X * organisations permission to use or modify this software.
X *
X * TFS supplies this software to be publicly redistributed
X * on the understanding that TFS is not responsible for the correct
X * functioning of this software in any circumstances.
X *
X */
X
X/*
X * Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
X */
X
X/*
X * SCSI command format
X */
Xstruct scsi_read_element_status
X{
X u_char op_code;
X u_char element_type_code:4;
X u_char voltag:1;
X u_char lun:3;
X u_char starting_element_addr[2];
X u_char number_of_elements[2];
X u_char resv1;
X u_char allocation_length[3];
X u_char resv2;
X u_char link:1;
X u_char flag:1;
X u_char :6;
X};
X#define RE_ALL_ELEMENTS 0
X#define RE_MEDIUM_TRANSPORT_ELEMENT 1
X#define RE_STORAGE_ELEMENT 2
X#define RE_IMPORT_EXPORT 3
X#define RE_DATA_TRANSFER_ELEMENT 4
X
Xstruct scsi_move_medium
X{
X u_char op_code;
X u_char :5;
X u_char lun:3;
X u_char transport_element_address[2];
X u_char source_address[2];
X u_char destination_address[2];
X u_char rsvd[2];
X u_char invert:1;
X u_char :7;
X u_char link:1;
X u_char flag:1;
X u_char :6;
X};
X
Xstruct scsi_position_to_element
X{
X u_char op_code;
X u_char :5;
X u_char lun:3;
X u_char transport_element_address[2];
X u_char source_address[2];
X u_char rsvd[2];
X u_char invert:1;
X u_char :7;
X u_char link:1;
X u_char flag:1;
X u_char :6;
X};
X
X/*
X * Opcodes
X */
X#define POSITION_TO_ELEMENT 0x2b
X#define MOVE_MEDIUM 0xa5
X#define READ_ELEMENT_STATUS 0xb8
X
Xstruct scsi_element_status_data
X{
X u_char first_element_reported[2];
X u_char number_of_elements_reported[2];
X u_char rsvd;
X u_char byte_count_of_report[3];
X};
X
Xstruct element_status_page
X{
X u_char element_type_code;
X u_char :5;
X u_char avoltag:1;
X u_char pvoltag:1;
X u_char element_descriptor_length[2];
X u_char rsvd;
X u_char byte_count_of_descriptor_data[3];
X};
X
END-of-scsi/scsi_changer.h
echo x - scsi/ch.c
sed 's/^X//' >scsi/ch.c << 'END-of-scsi/ch.c'
X/*
X * Mach Operating System
X * Copyright (c) 1990 Carnegie-Mellon University
X * Copyright (c) 1989 Carnegie-Mellon University
X * All rights reserved. The CMU software License Agreement specifies
X * the terms and conditions for use and redistribution.
X */
X/*
X * HISTORY
X *
X *
X */
X
X#include <sys/types.h>
X#include <ch.h>
X
X#include <sys/param.h>
X#include <sys/systm.h>
X
X#include <sys/errno.h>
X#include <sys/ioctl.h>
X#include <sys/buf.h>
X#include <sys/proc.h>
X#include <sys/user.h>
X#include <sys/chio.h>
X
X#if defined(OSF)
X#define SECSIZE 512
X#endif /* defined(OSF) */
X
X#include <scsi/scsi_all.h>
X#include <scsi/scsi_changer.h>
X#include <scsi/scsiconf.h>
X
X
Xstruct scsi_xfer ch_scsi_xfer[NCH];
Xint ch_xfer_block_wait[NCH];
X
X
X#define PAGESIZ 4096
X#define STQSIZE 4
X#define CH_RETRIES 4
X
X
X#define MODE(z) ( (minor(z) & 0x0F) )
X#define UNIT(z) ( (minor(z) >> 4) )
X
X#ifndef MACH
X#define ESUCCESS 0
X#endif MACH
X
Xint ch_info_valid[NCH]; /* the info about the device is valid */
Xint ch_initialized[NCH] ;
Xint ch_debug = 1;
X
Xint chattach();
Xint ch_done();
Xstruct ch_data
X{
X int flags;
X struct scsi_switch *sc_sw; /* address of scsi low level switch */
X int ctlr; /* so they know which one we want */
X int targ; /* our scsi target ID */
X int lu; /* out scsi lu */
X short chmo; /* Offset of first CHM */
X short chms; /* No. of CHM */
X short slots; /* No. of Storage Elements */
X short sloto; /* Offset of first SE */
X short imexs; /* No. of Import/Export Slots */
X short imexo; /* Offset of first IM/EX */
X short drives; /* No. of CTS */
X short driveo; /* Offset of first CTS */
X short rot; /* CHM can rotate */
X u_long op_matrix; /* possible opertaions */
X u_short lsterr; /* details of lasterror */
X u_char stor; /* posible Storage locations */
X}ch_data[NCH];
X
X#define CH_OPEN 0x01
X#define CH_KNOWN 0x02
X
Xstatic int next_ch_unit = 0;
X/***********************************************************************\
X* The routine called by the low level scsi routine when it discovers *
X* A device suitable for this driver *
X\***********************************************************************/
X
Xint chattach(ctlr,targ,lu,scsi_switch)
Xstruct scsi_switch *scsi_switch;
X{
X int unit,i,stat;
X unsigned char *tbl;
X
X if(scsi_debug & PRINTROUTINES) printf("chattach: ");
X /*******************************************************\
X * Check we have the resources for another drive *
X \*******************************************************/
X unit = next_ch_unit++;
X if( unit >= NCH)
X {
X printf("Too many scsi changers..(%d > %d) reconfigure kernel",(unit + 1),NCH);
X return(0);
X }
X /*******************************************************\
X * Store information needed to contact our base driver *
X \*******************************************************/
X ch_data[unit].sc_sw = scsi_switch;
X ch_data[unit].ctlr = ctlr;
X ch_data[unit].targ = targ;
X ch_data[unit].lu = lu;
X
X /*******************************************************\
X * Use the subdriver to request information regarding *
X * the drive. We cannot use interrupts yet, so the *
X * request must specify this. *
X \*******************************************************/
X if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/)))
X {
X printf(" ch%d: scsi changer, %d slot(s) %d drive(s) %d arm(s) %d i/e-slot(s) \n",
X unit, ch_data[unit].slots, ch_data[unit].drives, ch_data[unit].chms, ch_data[unit].imexs);
X stat=CH_KNOWN;
X }
X else
X {
X printf(" ch%d: scsi changer :- offline\n", unit);
X stat=CH_OPEN;
X }
X ch_initialized[unit] = stat;
X
X return;
X
X}
X
X
X
X/*******************************************************\
X* open the device. *
X\*******************************************************/
Xchopen(dev)
X{
X int errcode = 0;
X int unit,mode;
X
X unit = UNIT(dev);
X mode = MODE(dev);
X
X /*******************************************************\
X * Check the unit is legal *
X \*******************************************************/
X if ( unit >= NCH )
X {
X printf("ch %d > %d\n",unit,NCH);
X errcode = ENXIO;
X return(errcode);
X }
X /*******************************************************\
X * Only allow one at a time *
X \*******************************************************/
X if(ch_data[unit].flags & CH_OPEN)
X {
X printf("CH%d already open\n",unit);
X errcode = ENXIO;
X goto bad;
X }
X
X if(ch_debug||(scsi_debug & (PRINTROUTINES | TRACEOPENS)))
X printf("chopen: dev=0x%x (unit %d (of %d))\n"
X , dev, unit, NCH);
X /*******************************************************\
X * Make sure the device has been initialised *
X \*******************************************************/
X
X if (!ch_initialized[unit])
X return(ENXIO);
X if (ch_initialized[unit]!=CH_KNOWN) {
X if((ch_mode_sense(unit, SCSI_NOSLEEP | SCSI_NOMASK /*| SCSI_SILENT*/)))
X {
X ch_initialized[unit]=CH_KNOWN;
X }
X else
X {
X printf(" ch%d: scsi changer :- offline\n", unit);
X return(ENXIO);
X }
X }
X /*******************************************************\
X * Check that it is still responding and ok. *
X \*******************************************************/
X
X if(ch_debug || (scsi_debug & TRACEOPENS))
X printf("device is ");
X if (!(ch_req_sense(unit, 0)))
X {
X errcode = ENXIO;
X if(ch_debug || (scsi_debug & TRACEOPENS))
X printf("not responding\n");
X goto bad;
X }
X if(ch_debug || (scsi_debug & TRACEOPENS))
X printf("ok\n");
X
X if(!(ch_test_ready(unit,0)))
X {
X printf("ch%d not ready\n",unit);
X return(EIO);
X }
X
X ch_info_valid[unit] = TRUE;
X
X /*******************************************************\
X * Load the physical device parameters *
X \*******************************************************/
X
X ch_data[unit].flags = CH_OPEN;
X return(errcode);
Xbad:
X return(errcode);
X}
X
X/*******************************************************\
X* close the device.. only called if we are the LAST *
X* occurence of an open device *
X\*******************************************************/
Xchclose(dev)
X{
X unsigned char unit,mode;
X
X unit = UNIT(dev);
X mode = MODE(dev);
X
X if(scsi_debug & TRACEOPENS)
X printf("Closing device");
X ch_data[unit].flags = 0;
X return(0);
X}
X
X
X/*******************************************************\
X* Actually translate the requested transfer into *
X* one the physical driver can understand *
X* The transfer is described by a buf and will include *
X* only one physical transfer. *
X\*******************************************************/
X
Xint chstrategy(bp)
Xstruct buf *bp;
X{
X struct buf *dp;
X unsigned char unit;
X unsigned int opri;
X
X bp->b_error = EIO;
X
X bp->b_flags |= B_ERROR;
X
X /*******************************************************\
X * Correctly set the buf to indicate a completed xfer *
X \*******************************************************/
X iodone(bp);
X return;
X}
X
X
X/***************************************************************\
X* chstart *
X* This routine is also called after other non-queued requests *
X* have been made of the scsi driver, to ensure that the queue *
X* continues to be drained. *
X\***************************************************************/
X/* chstart() is called at splbio */
Xchstart(unit)
X{
X int drivecount;
X register struct buf *bp = 0;
X register struct buf *dp;
X struct scsi_xfer *xs;
X int blkno, nblk;
X
X
X if(scsi_debug & PRINTROUTINES) printf("chstart%d ",unit);
X /*******************************************************\
X * See if there is a buf to do and we are not already *
X * doing one *
X \*******************************************************/
X xs=&ch_scsi_xfer[unit];
X if(xs->flags & INUSE)
X {
X return; /* unit already underway */
X }
X if(ch_xfer_block_wait[unit]) /* a special awaits, let it proceed first */
X {
X wakeup(&ch_xfer_block_wait[unit]);
X return;
X }
X
X return;
X
X}
X
X
X/*******************************************************\
X* This routine is called by the scsi interrupt when *
X* the transfer is complete.
X\*******************************************************/
Xint ch_done(unit,xs)
Xint unit;
Xstruct scsi_xfer *xs;
X{
X struct buf *bp;
X int retval;
X
X if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("ch_done%d ",unit);
X if (! (xs->flags & INUSE))
X panic("scsi_xfer not in use!");
X wakeup(xs);
X}
X/*******************************************************\
X* Perform special action on behalf of the user *
X* Knows about the internals of this device *
X\*******************************************************/
Xchioctl(dev, cmd, arg, mode)
Xdev_t dev;
Xint cmd;
Xcaddr_t arg;
X{
X /* struct ch_cmd_buf *args;*/
X union scsi_cmd *scsi_cmd;
X register i,j;
X unsigned int opri;
X int errcode = 0;
X unsigned char unit;
X int number,flags,ret;
X
X /*******************************************************\
X * Find the device that the user is talking about *
X \*******************************************************/
X flags = 0; /* give error messages, act on errors etc. */
X unit = UNIT(dev);
X
X switch(cmd)
X {
X case CHIOOP: {
X struct chop *ch=(struct chop *) arg;
X if (ch_debug)
X printf("[chtape_chop: %x]\n", ch->ch_op);
X
X switch ((short)(ch->ch_op)) {
X case CHGETPARAM:
X ch->u.getparam.chmo= ch_data[unit].chmo;
X ch->u.getparam.chms= ch_data[unit].chms;
X ch->u.getparam.sloto= ch_data[unit].sloto;
X ch->u.getparam.slots= ch_data[unit].slots;
X ch->u.getparam.imexo= ch_data[unit].imexo;
X ch->u.getparam.imexs= ch_data[unit].imexs;
X ch->u.getparam.driveo= ch_data[unit].driveo;
X ch->u.getparam.drives= ch_data[unit].drives;
X ch->u.getparam.rot= ch_data[unit].rot;
X ch->result=0;
X return 0;
X break;
X case CHPOSITION:
X return ch_position(unit,&ch->result,ch->u.position.chm,
X ch->u.position.to,
X flags);
X case CHMOVE:
X return ch_move(unit,&ch->result, ch->u.position.chm,
X ch->u.move.from, ch->u.move.to,
X flags);
X case CHGETELEM:
X return ch_getelem(unit,&ch->result, ch->u.get_elem_stat.type,
X ch->u.get_elem_stat.from, &ch->u.get_elem_stat.elem_data,
X flags);
X default:
X return EINVAL;
X }
X
X }
X#ifdef NOT_NOW
X /*******************************************************\
X * This is a direct command for the scsi driver, pass *
X * it on.. *
X \*******************************************************/
X case A_CMD:
X args = (struct ch_cmd_buf *) arg;
X ch_cmd(args->byte[0], args->byte[1], args->byte, args->byte[3], args->byte[4], args->byte[5], args->byte[6], args->byte[7]);
X break;
X /*******************************************************\
X * This is a low level scsi command ... just do it *
X \*******************************************************/
X case A_SCSI:
X /* will not work yet... fix it later....
X errcode = ch_scsi_cmd(UNIT(dev),
X arg,
X /* len */ 10,
X ccb_data,
X sizeof(ccb_data),
X 100000,
X 0);
X break;
X#endif NOT_NOW
X default:
X return EINVAL;
X }
X
X return(ret?ESUCCESS:EIO);
X}
X
Xch_getelem(unit,stat,type,from,data,flags)
Xint unit,from,flags;
Xshort *stat;
Xchar *data;
X{
X struct scsi_read_element_status scsi_cmd;
X char elbuf[32];
X int ret;
X
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = READ_ELEMENT_STATUS;
X scsi_cmd.element_type_code=type;
X scsi_cmd.starting_element_addr[0]=(from>>8)&0xff;
X scsi_cmd.starting_element_addr[1]=from&0xff;
X scsi_cmd.number_of_elements[1]=1;
X scsi_cmd.allocation_length[2]=32;
X
X if ((ret=ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(scsi_cmd),
X elbuf,
X 32,
X 100000,
X flags) !=ESUCCESS)) {
X *stat=ch_data[unit].lsterr;
X bcopy(elbuf+16,data,16);
X return ret;
X }
X bcopy(elbuf+16,data,16); /*Just a hack sh */
X return ret;
X}
X
Xch_move(unit,stat,chm,from,to,flags)
Xint unit,chm,from,to,flags;
Xshort *stat;
X{
X struct scsi_move_medium scsi_cmd;
X int ret;
X
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = MOVE_MEDIUM;
X scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
X scsi_cmd.transport_element_address[1]=chm&0xff;
X scsi_cmd.source_address[0]=(from>>8)&0xff;
X scsi_cmd.source_address[1]=from&0xff;
X scsi_cmd.destination_address[0]=(to>>8)&0xff;
X scsi_cmd.destination_address[1]=to&0xff;
X scsi_cmd.invert=(chm&CH_INVERT)?1:0;
X if ((ret=ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(scsi_cmd),
X NULL,
X 0,
X 100000,
X flags) !=ESUCCESS)) {
X *stat=ch_data[unit].lsterr;
X return ret;
X }
X return ret;
X}
X
Xch_position(unit,stat,chm,to,flags)
Xint unit,chm,to,flags;
Xshort *stat;
X{
X struct scsi_position_to_element scsi_cmd;
X int ret;
X
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = POSITION_TO_ELEMENT;
X scsi_cmd.transport_element_address[0]=(chm>>8)&0xff;
X scsi_cmd.transport_element_address[1]=chm&0xff;
X scsi_cmd.source_address[0]=(to>>8)&0xff;
X scsi_cmd.source_address[1]=to&0xff;
X scsi_cmd.invert=(chm&CH_INVERT)?1:0;
X if ((ret=ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(scsi_cmd),
X NULL,
X 0,
X 100000,
X flags) !=ESUCCESS)) {
X *stat=ch_data[unit].lsterr;
X return ret;
X }
X return ret;
X}
X
X/*******************************************************\
X* Check with the device that it is ok, (via scsi driver)*
X\*******************************************************/
Xch_req_sense(unit, flags)
Xint flags;
X{
X struct scsi_sense_data sense;
X struct scsi_sense scsi_cmd;
X
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = REQUEST_SENSE;
X scsi_cmd.length = sizeof(sense);
X
X if (ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(struct scsi_sense),
X &sense,
X sizeof(sense),
X 100000,
X flags | SCSI_DATA_IN) != 0)
X {
X return(FALSE);
X }
X else
X return(TRUE);
X}
X
X/*******************************************************\
X* Get scsi driver to send a "are you ready" command *
X\*******************************************************/
Xch_test_ready(unit,flags)
Xint unit,flags;
X{
X struct scsi_test_unit_ready scsi_cmd;
X
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = TEST_UNIT_READY;
X
X if (ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(struct scsi_test_unit_ready),
X 0,
X 0,
X 100000,
X flags) != 0) {
X return(FALSE);
X } else
X return(TRUE);
X}
X
X
X#ifdef __STDC__
X#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
X#else
X#define b2tol(a) (((unsigned)(a/**/_1) << 8) + (unsigned)a/**/_0 )
X#endif
X
X/*******************************************************\
X* Get the scsi driver to send a full inquiry to the *
X* device and use the results to fill out the global *
X* parameter structure. *
X\*******************************************************/
Xch_mode_sense(unit, flags)
Xint unit,flags;
X{
X struct scsi_mode_sense scsi_cmd;
X u_char scsi_sense[128]; /* Can't use scsi_mode_sense_data because of */
X /* missing block descriptor */
X u_char *b;
X int i,l;
X
X /*******************************************************\
X * First check if we have it all loaded *
X \*******************************************************/
X if (ch_info_valid[unit]==CH_KNOWN) return(TRUE);
X /*******************************************************\
X * First do a mode sense *
X \*******************************************************/
X ch_info_valid[unit] &= ~CH_KNOWN;
X for(l=1;l>=0;l--) {
X bzero(&scsi_cmd, sizeof(scsi_cmd));
X scsi_cmd.op_code = MODE_SENSE;
X scsi_cmd.dbd = l;
X scsi_cmd.page_code = 0x3f; /* All Pages */
X scsi_cmd.length = sizeof(scsi_sense);
X /*******************************************************\
X * do the command, but we don't need the results *
X * just print them for our interest's sake *
X \*******************************************************/
X if (ch_scsi_cmd(unit,
X &scsi_cmd,
X sizeof(struct scsi_mode_sense),
X &scsi_sense,
X sizeof(scsi_sense),
X 5000,
X flags | SCSI_DATA_IN) == 0) {
X ch_info_valid[unit] = CH_KNOWN;
X break;
X }
X }
X if (ch_info_valid[unit]!=CH_KNOWN) {
X if(!(flags & SCSI_SILENT))
X printf("could not mode sense for unit %d\n", unit);
X return(FALSE);
X }
X l=scsi_sense[0]-3;
X b=&scsi_sense[4];
X /*****************************\
X * To avoid alignment problems *
X \*****************************/
X/*FIX THIS FOR MSB */
X#define p2copy(valp) (valp[1]+ (valp[0]<<8));valp+=2
X#define p4copy(valp) (valp[3]+ (valp[2]<<8) + (valp[1]<<16) + (valp[0]<<24));valp+=4
X#if 0
X printf("\nmode_sense %d\n",l);
X for(i=0;i<l+4;i++) {
X printf("%x%c",scsi_sense[i],i%8==7?'\n':':');
X }
X printf("\n");
X#endif
X for(i=0;i<l;) {
X int pc=(*b++)&0x3f;
X int pl=*b++;
X u_char *bb=b;
X switch(pc) {
X case 0x1d:
X ch_data[unit].chmo =p2copy(bb);
X ch_data[unit].chms =p2copy(bb);
X ch_data[unit].sloto =p2copy(bb);
X ch_data[unit].slots =p2copy(bb);
X ch_data[unit].imexo =p2copy(bb);
X ch_data[unit].imexs =p2copy(bb);
X ch_data[unit].driveo =p2copy(bb);
X ch_data[unit].drives =p2copy(bb);
X break;
X case 0x1e:
X ch_data[unit].rot = (*b)&1;
X break;
X case 0x1f:
X ch_data[unit].stor = *b&0xf;
X bb+=2;
X ch_data[unit].stor =p4copy(bb);
X break;
X default:
X break;
X }
X b+=pl;
X i+=pl+2;
X }
X if (ch_debug)
X {
X printf("unit %d: cht(%d-%d)slot(%d-%d)imex(%d-%d)cts(%d-%d) %s rotate\n",
X unit,
X ch_data[unit].chmo,
X ch_data[unit].chms,
X ch_data[unit].sloto,
X ch_data[unit].slots,
X ch_data[unit].imexo,
X ch_data[unit].imexs,
X ch_data[unit].driveo,
X ch_data[unit].drives,
X ch_data[unit].rot?"can":"can't");
X }
X return(TRUE);
X}
X
X/*******************************************************\
X* ask the scsi driver to perform a command for us. *
X* Call it through the switch table, and tell it which *
X* sub-unit we want, and what target and lu we wish to *
X* talk to. Also tell it where to find the command *
X* how long int is. *
X* Also tell it where to read/write the data, and how *
X* long the data is supposed to be *
X\*******************************************************/
Xint ch_scsi_cmd(unit,scsi_cmd,cmdlen,data_addr,datalen,timeout,flags)
X
Xint unit,flags;
Xstruct scsi_generic *scsi_cmd;
Xint cmdlen;
Xint timeout;
Xu_char *data_addr;
Xint datalen;
X{
X struct scsi_xfer *xs;
X int retval;
X int s;
X
X if(ch_debug||(scsi_debug & PRINTROUTINES)) printf("\nch_scsi_cmd%d %x",
X unit,scsi_cmd->opcode);
X if(ch_data[unit].sc_sw) /* If we have a scsi driver */
X {
X
X xs = &(ch_scsi_xfer[unit]);
X if(!(flags & SCSI_NOMASK))
X s = splbio();
X ch_xfer_block_wait[unit]++; /* there is someone waiting */
X while (xs->flags & INUSE)
X {
X sleep(&ch_xfer_block_wait[unit],PRIBIO+1);
X }
X ch_xfer_block_wait[unit]--;
X xs->flags = INUSE;
X if(!(flags & SCSI_NOMASK))
X splx(s);
X
X /*******************************************************\
X * Fill out the scsi_xfer structure *
X \*******************************************************/
X xs->flags |= flags;
X xs->adapter = ch_data[unit].ctlr;
X xs->targ = ch_data[unit].targ;
X xs->lu = ch_data[unit].lu;
X xs->retries = CH_RETRIES;
X xs->timeout = timeout;
X xs->cmd = scsi_cmd;
X xs->cmdlen = cmdlen;
X xs->data = data_addr;
X xs->datalen = datalen;
X xs->resid = datalen;
X xs->when_done = (flags & SCSI_NOMASK)
X ?(int (*)())0
X :ch_done;
X xs->done_arg = unit;
X xs->done_arg2 = (int)xs;
Xretry: xs->error = XS_NOERROR;
X xs->bp = 0;
X ch_data[unit].lsterr=0;
X retval = (*(ch_data[unit].sc_sw->scsi_cmd))(xs);
X switch(retval)
X {
X case SUCCESSFULLY_QUEUED:
X while(!(xs->flags & ITSDONE))
X sleep(xs,PRIBIO+1);
X
X case HAD_ERROR:
X case COMPLETE:
X switch(xs->error)
X {
X case XS_NOERROR:
X retval = ESUCCESS;
X break;
X case XS_SENSE:
X retval = (ch_interpret_sense(unit,xs));
X break;
X case XS_DRIVER_STUFFUP:
X retval = EIO;
X break;
X case XS_TIMEOUT:
X if(xs->retries-- )
X {
X xs->flags &= ~ITSDONE;
X goto retry;
X }
X retval = EIO;
X break;
X case XS_BUSY:
X if(xs->retries-- )
X {
X xs->flags &= ~ITSDONE;
X goto retry;
X }
X retval = EIO;
X break;
X default:
X retval = EIO;
X printf("st%d: unknown error category from scsi driver\n"
X ,unit);
X break;
X }
X break;
X case TRY_AGAIN_LATER:
X if(xs->retries-- )
X {
X xs->flags &= ~ITSDONE;
X goto retry;
X }
X retval = EIO;
X break;
X default:
X retval = EIO;
X }
X xs->flags = 0; /* it's free! */
X chstart(unit);
X }
X else
X {
X printf("chd: not set up\n",unit);
X return(EINVAL);
X }
X return(retval);
X}
X/***************************************************************\
X* Look at the returned sense and act on the error and detirmine *
X* The unix error number to pass back... (0 = report no error) *
X\***************************************************************/
X
Xint ch_interpret_sense(unit,xs)
Xint unit;
Xstruct scsi_xfer *xs;
X{
X struct scsi_sense_data *sense;
X int key;
X int silent = xs->flags & SCSI_SILENT;
X
X /***************************************************************\
X * If errors are ok, report a success *
X \***************************************************************/
X if(xs->flags & SCSI_ERR_OK) return(ESUCCESS);
X
X /***************************************************************\
X * Get the sense fields and work out what CLASS *
X \***************************************************************/
X sense = &(xs->sense);
X switch(sense->error_class)
X {
X /***************************************************************\
X * If it's class 7, use the extended stuff and interpret the key *
X \***************************************************************/
X case 7:
X {
X key=sense->ext.extended.sense_key;
X if(sense->ext.extended.ili)
X if(!silent)
X {
X printf("length error ");
X }
X if(sense->valid)
X xs->resid = ntohl(*((long *)sense->ext.extended.info));
X if(xs->bp)
X {
X xs->bp->b_flags |= B_ERROR;
X return(ESUCCESS);
X }
X if(sense->ext.extended.eom)
X if(!silent) printf("end of medium ");
X if(sense->ext.extended.filemark)
X if(!silent) printf("filemark ");
X if(ch_debug)
X {
X printf("code%x class%x valid%x\n"
X ,sense->error_code
X ,sense->error_class
X ,sense->valid);
X printf("seg%x key%x ili%x eom%x fmark%x\n"
X ,sense->ext.extended.segment
X ,sense->ext.extended.sense_key
X ,sense->ext.extended.ili
X ,sense->ext.extended.eom
X ,sense->ext.extended.filemark);
X printf("info: %x %x %x %x followed by %d extra bytes\n"
X ,sense->ext.extended.info[0]
X ,sense->ext.extended.info[1]
X ,sense->ext.extended.info[2]
X ,sense->ext.extended.info[3]
X ,sense->ext.extended.extra_len);
X printf("extra: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n"
X ,sense->ext.extended.extra_bytes[0]
X ,sense->ext.extended.extra_bytes[1]
X ,sense->ext.extended.extra_bytes[2]
X ,sense->ext.extended.extra_bytes[3]
X ,sense->ext.extended.extra_bytes[4]
X ,sense->ext.extended.extra_bytes[5]
X ,sense->ext.extended.extra_bytes[6]
X ,sense->ext.extended.extra_bytes[7]
X ,sense->ext.extended.extra_bytes[8]
X ,sense->ext.extended.extra_bytes[9]
X ,sense->ext.extended.extra_bytes[10]
X ,sense->ext.extended.extra_bytes[11]
X ,sense->ext.extended.extra_bytes[12]
X ,sense->ext.extended.extra_bytes[13]
X ,sense->ext.extended.extra_bytes[14]
X ,sense->ext.extended.extra_bytes[15]);
X
X }
X switch(key)
X {
X case 0x0:
X return(ESUCCESS);
X case 0x1:
X if(!silent)
X {
X printf("st%d: soft error(corrected) ", unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(ESUCCESS);
X case 0x2:
X if(!silent) printf("st%d: not ready\n ", unit);
X ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X sense->ext.extended.info[13] ;
X return(ENODEV);
X case 0x3:
X if(!silent)
X {
X printf("st%d: medium error ", unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(EIO);
X case 0x4:
X if(!silent) printf("st%d: non-media hardware failure\n ",
X unit);
X ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X sense->ext.extended.info[13] ;
X return(EIO);
X case 0x5:
X if(!silent) printf("st%d: illegal request\n ", unit);
X ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X sense->ext.extended.info[13] ;
X return(EINVAL);
X case 0x6:
X if(!silent) printf("st%d: Unit attention.\n ", unit);
X ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X sense->ext.extended.info[13] ;
X ch_info_valid[unit] = FALSE;
X if (ch_data[unit].flags & CH_OPEN) /* TEMP!!!! */
X return(EIO);
X else
X return(ESUCCESS);
X case 0x7:
X if(!silent)
X {
X printf("st%d: attempted protection violation "
X , unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(EACCES);
X case 0x8:
X if(!silent)
X {
X printf("st%d: block wrong state (worm)\n "
X , unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(EIO);
X case 0x9:
X if(!silent) printf("st%d: vendor unique\n",
X unit);
X return(EIO);
X case 0xa:
X if(!silent) printf("st%d: copy aborted\n ",
X unit);
X return(EIO);
X case 0xb:
X if(!silent) printf("st%d: command aborted\n ",
X unit);
X ch_data[unit].lsterr=(sense->ext.extended.info[12]<<8)|
X sense->ext.extended.info[13] ;
X return(EIO);
X case 0xc:
X if(!silent)
X {
X printf("st%d: search returned\n ", unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(ESUCCESS);
X case 0xd:
X if(!silent) printf("st%d: volume overflow\n ",
X unit);
X return(ENOSPC);
X case 0xe:
X if(!silent)
X {
X printf("st%d: verify miscompare\n ", unit);
X if(sense->valid)
X {
X printf("block no. %d (decimal)\n",
X (sense->ext.extended.info[0] <<24)|
X (sense->ext.extended.info[1] <<16)|
X (sense->ext.extended.info[2] <<8)|
X (sense->ext.extended.info[3] ));
X }
X else
X {
X printf("\n");
X }
X }
X return(EIO);
X case 0xf:
X if(!silent) printf("st%d: unknown error key\n ",
X unit);
X return(EIO);
X }
X break;
X }
X /***************************************************************\
X * If it's NOT class 7, just report it. *
X \***************************************************************/
X case 0:
X case 1:
X case 2:
X case 3:
X case 4:
X case 5:
X case 6:
X {
X if(!silent) printf("st%d: error class %d code %d\n",
X unit,
X sense->error_class,
X sense->error_code);
X if(sense->valid)
X if(!silent) printf("block no. %d (decimal)\n",
X (sense->ext.unextended.blockhi <<16),
X + (sense->ext.unextended.blockmed <<8),
X + (sense->ext.unextended.blocklow ));
X }
X return(EIO);
X }
X}
X
X
X
END-of-scsi/ch.c
exit