/* */ /* * HISTORY * * * PATCHES MAGIC LEVEL PATCH THAT GOT US HERE * -------------------- ----- ---------------------- * CURRENT PATCH LEVEL: 1 00098 * -------------------- ----- ---------------------- * * 16 Feb 93 Julian Elischer ADDED for SCSI system * */ #include #include #include #include #include #include #include #include #include #include #if defined(OSF) #define SECSIZE 512 #endif /* defined(OSF) */ #include #include #include struct scsi_xfer ch_scsi_xfer[NCH]; int ch_xfer_block_wait[NCH]; #define PAGESIZ 4096 #define STQSIZE 4 #define CH_RETRIES 4 #define MODE(z) ( (minor(z) & 0x0F) ) #define UNIT(z) ( (minor(z) >> 4) ) #ifndef MACH #define ESUCCESS 0 #endif MACH int ch_info_valid[NCH]; /* the info about the device is valid */ int ch_initialized[NCH] ; int ch_debug = 1; int chattach(); int ch_done(); struct ch_data { 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; { 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; return; } /*******************************************************\ * open the device. * \*******************************************************/ chopen(dev) { 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"); 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); } ch_info_valid[unit] = TRUE; /*******************************************************\ * Load the physical device parameters * \*******************************************************/ ch_data[unit].flags = CH_OPEN; return(errcode); bad: return(errcode); } /*******************************************************\ * close the device.. only called if we are the LAST * * occurence of an open device * \*******************************************************/ chclose(dev) { unsigned char unit,mode; unit = UNIT(dev); mode = MODE(dev); if(scsi_debug & TRACEOPENS) printf("Closing device"); ch_data[unit].flags = 0; return(0); } /***************************************************************\ * 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; } return; } /*******************************************************\ * 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; 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; } return(ret?ESUCCESS:EIO); } ch_getelem(unit,stat,type,from,data,flags) int unit,from,flags; short *stat; char *data; { 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; } ch_move(unit,stat,chm,from,to,flags) int unit,chm,from,to,flags; short *stat; { 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; } ch_position(unit,stat,chm,to,flags) int unit,chm,to,flags; short *stat; { 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; } /*******************************************************\ * Check with the device that it is ok, (via scsi driver)* \*******************************************************/ ch_req_sense(unit, flags) int flags; { 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); } else return(TRUE); } /*******************************************************\ * Get scsi driver to send a "are you ready" command * \*******************************************************/ ch_test_ready(unit,flags) int unit,flags; { 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); } #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; { 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;iopcode); 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); } else { printf("chd: not set up\n",unit); return(EINVAL); } return(retval); } /***************************************************************\ * 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; { 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); } }