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 7 of 10) Message-ID: <1992Oct3.040218.13917@tfs.com> Organization: TRW Financial Systems Date: Sat, 3 Oct 1992 04:02:18 GMT Lines: 1728 # 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: # # scsi/cd.c # echo x - scsi/cd.c sed 's/^X//' >scsi/cd.c << 'END-of-scsi/cd.c' X/* X * Written by Julian Elischer (julian@tfs.com) X * for TRW Financial Systems for use under the MACH(2.5) operating system. 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/* RCS header and log section X * $Log: $ X */ X#define SPLCD splbio X#define ESUCCESS 0 X#include <cd.h> X#include <sys/types.h> X#include <sys/param.h> X#include <sys/dkbad.h> X#include <sys/systm.h> X#include <sys/conf.h> X#include <sys/file.h> X#include <sys/stat.h> X#include <sys/ioctl.h> X#include <sys/buf.h> X#include <sys/uio.h> X#include <sys/malloc.h> X#include <sys/cdio.h> X X#include <sys/errno.h> X#include <sys/disklabel.h> X#include <scsi/scsi_all.h> X#include <scsi/scsi_cd.h> X#include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */ X#include <scsi/scsiconf.h> X Xlong int cdstrats,cdqueues; X X X#include <ddb.h> X#if NDDB > 0 Xint Debugger(); X#else NDDB > 0 X#define Debugger() X#endif NDDB > 0 X X X#define PAGESIZ 4096 X#define SECSIZE 2048 /* XXX */ /* default only */ X#define CDOUTSTANDING 2 X#define CDQSIZE 4 X#define CD_RETRIES 4 X X#define UNITSHIFT 3 X#define PARTITION(z) (minor(z) & 0x07) X#define RAW_PART 3 X#define UNIT(z) ( (minor(z) >> UNITSHIFT) ) X X Xextern int hz; Xint cd_done(); Xint cdstrategy(); Xint cd_debug = 0; X Xstruct buf cd_buf_queue[NCD]; Xstruct scsi_xfer cd_scsi_xfer[NCD][CDOUTSTANDING]; /* XXX */ Xstruct scsi_xfer *cd_free_xfer[NCD]; Xint cd_xfer_block_wait[NCD]; X Xstruct cd_data X{ X int flags; X#define CDVALID 0x02 /* PARAMS LOADED */ X#define CDINIT 0x04 /* device has been init'd */ X#define CDWAIT 0x08 /* device has someone waiting */ X#define CDHAVELABEL 0x10 /* have read the label */ 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 int cmdscount; /* cmds allowed outstanding by board*/ X struct cd_parms X { X int blksize; X u_long disksize; /* total number sectors */ X }params; X struct disklabel disklabel; X int partflags[MAXPARTITIONS]; /* per partition flags */ X#define CDOPEN 0x01 X int openparts; /* one bit for each open partition */ X}cd_data[NCD]; X X#define CD_STOP 0 X#define CD_START 1 X#define CD_EJECT -2 X X Xstatic int next_cd_unit = 0; X/***********************************************************************\ X* The routine called by the low level scsi routine when it discovers * X* A device suitable for this driver * X\***********************************************************************/ Xint cdattach(ctlr,targ,lu,scsi_switch) Xstruct scsi_switch *scsi_switch; X{ X int unit,i; X unsigned char *tbl; X struct cd_data *cd; X struct cd_parms *dp; X X unit = next_cd_unit++; X cd = cd_data + unit; X dp = &(cd->params); X if(scsi_debug & PRINTROUTINES) printf("cdattach: "); X /*******************************************************\ X * Check we have the resources for another drive * X \*******************************************************/ X if( unit >= NCD) X { X printf("Too many scsi CDs..(%d > %d) reconfigure kernel",(unit + 1),NCD); X return(0); X } X /*******************************************************\ X * Store information needed to contact our base driver * X \*******************************************************/ X cd->sc_sw = scsi_switch; X cd->ctlr = ctlr; X cd->targ = targ; X cd->lu = lu; X cd->cmdscount = CDOUTSTANDING; /* XXX (ask the board) */ X X X i = cd->cmdscount; X while(i-- ) X { X cd_scsi_xfer[unit][i].next = cd_free_xfer[unit]; X cd_free_xfer[unit] = &cd_scsi_xfer[unit][i]; 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 cd_get_parms(unit, SCSI_NOSLEEP | SCSI_NOMASK); X if(dp->disksize) X { X printf("cd present\n"); X } X else X { X printf("drive empty\n"); X } X cd->flags |= CDINIT; X return; X X} X X X X/*******************************************************\ X* open the device. Make sure the partition info * X* is a up-to-date as can be. * X\*******************************************************/ Xcdopen(dev) X{ X int errcode = 0; X int unit, part; X struct cd_parms cd_parms; X struct cd_data *cd ; X X unit = UNIT(dev); X part = PARTITION(dev); X cd = cd_data + unit; X if(scsi_debug & (PRINTROUTINES | TRACEOPENS)) X printf("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n" X , dev, unit, NCD, part); X /*******************************************************\ X * Check the unit is legal * X \*******************************************************/ X if ( unit >= NCD ) X { X return(ENXIO); X } X /*******************************************************\ X * Make sure the disk has been initialised * X * At some point in the future, get the scsi driver * X * to look for a new device if we are not initted * X \*******************************************************/ X if (! (cd->flags & CDINIT)) X return(ENXIO); X X /*******************************************************\ X * If it's been invalidated, and not everybody has * X * closed it then forbid re-entry. * X * (may have changed media) * X \*******************************************************/ X if ((! (cd->flags & CDVALID)) X && ( cd->openparts)) X return(ENXIO); X /*******************************************************\ X * Check that it is still responding and ok. * X * if the media has been changed this will result in a * X * "unit attention" error which the error code will * X * disregard because the CDVALID flag is not yet set * X \*******************************************************/ X if (cd_req_sense(unit, SCSI_SILENT) != 0) { X if(scsi_debug & TRACEOPENS) X printf("not reponding\n"); X return(ENXIO); X } X if(scsi_debug & TRACEOPENS) X printf("Device present\n"); X /*******************************************************\ X * In case it is a funny one, tell it to start * X * not needed for hard drives * X \*******************************************************/ X cd_start_unit(unit,part,CD_START); X cd_prevent_unit(unit,PR_PREVENT,SCSI_SILENT); X if(scsi_debug & TRACEOPENS) X printf("started "); X /*******************************************************\ X * Load the physical device parameters * X \*******************************************************/ X cd_get_parms(unit, 0); X if(scsi_debug & TRACEOPENS) X printf("Params loaded "); X /*******************************************************\ X * Load the partition info if not already loaded * X \*******************************************************/ X cdgetdisklabel(unit); X if(scsi_debug & TRACEOPENS) X printf("Disklabel fabricated "); X /*******************************************************\ X * Check the partition is legal * X \*******************************************************/ X if (( part >= cd->disklabel.d_npartitions ) X && (part != RAW_PART)) X { X if(scsi_debug & TRACEOPENS) X printf("partition %d > %d\n",part X ,cd->disklabel.d_npartitions); X cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); X return(ENXIO); X } X /*******************************************************\ X * Check that the partition exists * X \*******************************************************/ X if (( cd->disklabel.d_partitions[part].p_fstype != FS_UNUSED ) X || (part == RAW_PART)) X { X cd->partflags[part] |= CDOPEN; X cd->openparts |= (1 << part); X if(scsi_debug & TRACEOPENS) X printf("open complete\n"); X cd->flags |= CDVALID; X } X else X { X if(scsi_debug & TRACEOPENS) X printf("part %d type UNUSED\n",part); X cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); X return(ENXIO); X } X return(0); X} X X/*******************************************************\ X* Get ownership of a scsi_xfer structure * X* If need be, sleep on it, until it comes free * X\*******************************************************/ Xstruct scsi_xfer *cd_get_xs(unit,flags) Xint flags; Xint unit; X{ X struct scsi_xfer *xs; X int s; X X if(flags & (SCSI_NOSLEEP | SCSI_NOMASK)) X { X if (xs = cd_free_xfer[unit]) X { X cd_free_xfer[unit] = xs->next; X xs->flags = 0; X } X } X else X { X s = SPLCD(); X while (!(xs = cd_free_xfer[unit])) X { X cd_xfer_block_wait[unit]++; /* someone waiting! */ X sleep((caddr_t)&cd_free_xfer[unit], PRIBIO+1); X cd_xfer_block_wait[unit]--; X } X cd_free_xfer[unit] = xs->next; X splx(s); X xs->flags = 0; X } X return(xs); X} X X/*******************************************************\ X* Free a scsi_xfer, wake processes waiting for it * X\*******************************************************/ Xcd_free_xs(unit,xs,flags) Xstruct scsi_xfer *xs; Xint unit; Xint flags; X{ X int s; X X if(flags & SCSI_NOMASK) X { X if (cd_xfer_block_wait[unit]) X { X printf("doing a wakeup from NOMASK mode\n"); X wakeup((caddr_t)&cd_free_xfer[unit]); X } X xs->next = cd_free_xfer[unit]; X cd_free_xfer[unit] = xs; X } X else X { X s = SPLCD(); X if (cd_xfer_block_wait[unit]) X wakeup((caddr_t)&cd_free_xfer[unit]); X xs->next = cd_free_xfer[unit]; X cd_free_xfer[unit] = xs; X splx(s); X } X} X X/*******************************************************\ X* trim the size of the transfer if needed, * X* called by physio * X* basically the smaller of our max and the scsi driver's* X* minphys (note we have no max ourselves) * X\*******************************************************/ X/* Trim buffer length if buffer-size is bigger than page size */ Xvoid cdminphys(bp) Xstruct buf *bp; X{ X (*(cd_data[UNIT(bp->b_dev)].sc_sw->scsi_minphys))(bp); 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 cdstrategy(bp) Xstruct buf *bp; X{ X struct buf *dp; X unsigned int opri; X struct cd_data *cd ; X int unit; X X cdstrats++; X unit = UNIT((bp->b_dev)); X cd = cd_data + unit; X if(scsi_debug & PRINTROUTINES) printf("\ncdstrategy "); X if(scsi_debug & SHOWREQUESTS) printf("cd%d: %d bytes @ blk%d\n", X unit,bp->b_bcount,bp->b_blkno); X cdminphys(bp); X /*******************************************************\ X * If the device has been made invalid, error out * X * maybe the media changed * X \*******************************************************/ X if(!(cd->flags & CDVALID)) X { X bp->b_error = EIO; X goto bad; X } X /*******************************************************\ X * can't ever write to a CD * X \*******************************************************/ X if ((bp->b_flags & B_READ) == 0) { X bp->b_error = EROFS; X goto bad; X } X /*******************************************************\ X * If it's a null transfer, return immediatly * X \*******************************************************/ X if (bp->b_bcount == 0) { X goto done; X } X X /*******************************************************\ X * Decide which unit and partition we are talking about * X \*******************************************************/ X if(PARTITION(bp->b_dev) != RAW_PART) X { X if (!(cd->flags & CDHAVELABEL)) X { X bp->b_error = EIO; X goto bad; X } X /* X * do bounds checking, adjust transfer. if error, process. X * if end of partition, just return X */ X if (bounds_check_with_label(bp,&cd->disklabel,1) <= 0) X goto done; X /* otherwise, process transfer request */ X } X X opri = SPLCD(); X dp = &cd_buf_queue[unit]; X X /*******************************************************\ X * Place it in the queue of disk activities for this disk* X \*******************************************************/ X disksort(dp, bp); X X /*******************************************************\ X * Tell the device to get going on the transfer if it's * X * not doing anything, otherwise just wait for completion* X \*******************************************************/ X cdstart(unit); X X splx(opri); X return; Xbad: X bp->b_flags |= B_ERROR; Xdone: X X /*******************************************************\ X * Correctly set the buf to indicate a completed xfer * X \*******************************************************/ X bp->b_resid = bp->b_bcount; X biodone(bp); X return; X} X X/***************************************************************\ X* cdstart looks to see if there is a buf waiting for the device * X* and that the device is not already busy. If both are true, * X* It deques the buf and creates a scsi command to perform the * X* transfer in the buf. The transfer request will call cd_done * X* on completion, which will in turn call this routine again * X* so that the next queued transfer is performed. * X* The bufs are queued by the strategy routine (cdstrategy) * X* * 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* must be called at the correct (highish) spl level * X\***************************************************************/ X/* cdstart() is called at SPLCD from cdstrategy and cd_done*/ Xcdstart(unit) Xint unit; X{ X register struct buf *bp = 0; X register struct buf *dp; X struct scsi_xfer *xs; X struct scsi_rw_big cmd; X int blkno, nblk; X struct cd_data *cd = cd_data + unit; X struct partition *p ; X X if(scsi_debug & PRINTROUTINES) printf("cdstart%d ",unit); X /*******************************************************\ X * See if there is a buf to do and we are not already * X * doing one * X \*******************************************************/ X if(!cd_free_xfer[unit]) X { X return; /* none for us, unit already underway */ X } X X if(cd_xfer_block_wait[unit]) /* there is one, but a special waits */ X { X return; /* give the special that's waiting a chance to run */ X } X X X dp = &cd_buf_queue[unit]; X if ((bp = dp->b_actf) != NULL) /* yes, an assign */ X { X dp->b_actf = bp->av_forw; X } X else X { X return; X } X X xs=cd_get_xs(unit,0); /* ok we can grab it */ X xs->flags = INUSE; /* Now ours */ X /***************************************************************\ X * Should reject all queued entries if CDVALID is not true * X \***************************************************************/ X if(!(cd->flags & CDVALID)) X { X goto bad; /* no I/O.. media changed or something */ X } X X /*******************************************************\ X * We have a buf, now we should move the data into * X * a scsi_xfer definition and try start it * X \*******************************************************/ X /*******************************************************\ X * First, translate the block to absolute * X * and put it in terms of the logical blocksize of the * X * device.. * X \*******************************************************/ X p = cd->disklabel.d_partitions + PARTITION(bp->b_dev); X blkno = ((bp->b_blkno / (cd->params.blksize/512)) + p->p_offset); X nblk = (bp->b_bcount + (cd->params.blksize - 1)) / (cd->params.blksize); X X /*******************************************************\ X * Fill out the scsi command * X \*******************************************************/ X bzero(&cmd, sizeof(cmd)); X cmd.op_code = READ_BIG; X cmd.addr_3 = (blkno & 0xff000000) >> 24; X cmd.addr_2 = (blkno & 0xff0000) >> 16; X cmd.addr_1 = (blkno & 0xff00) >> 8; X cmd.addr_0 = blkno & 0xff; X cmd.length2 = (nblk & 0xff00) >> 8; X cmd.length1 = (nblk & 0xff); X /*******************************************************\ X * Fill out the scsi_xfer structure * X * Note: we cannot sleep as we may be an interrupt * X \*******************************************************/ X xs->flags |= SCSI_NOSLEEP; X xs->adapter = cd->ctlr; X xs->targ = cd->targ; X xs->lu = cd->lu; X xs->retries = CD_RETRIES; X xs->timeout = 10000;/* 10000 millisecs for a disk !*/ X xs->cmd = (struct scsi_generic *)&cmd; X xs->cmdlen = sizeof(cmd); X xs->resid = bp->b_bcount; X xs->when_done = cd_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; X xs->error = XS_NOERROR; X xs->bp = bp; X xs->data = (u_char *)bp->b_un.b_addr; X xs->datalen = bp->b_bcount; X X /*******************************************************\ X * Pass all this info to the scsi driver. * X \*******************************************************/ X if ( (*(cd->sc_sw->scsi_cmd))(xs) != SUCCESSFULLY_QUEUED) X { X printf("cd%d: oops not queued",unit); X goto bad; X } X cdqueues++; X return; Xbad: xs->error = XS_DRIVER_STUFFUP; X cd_done(unit,xs); X} X X/*******************************************************\ X* This routine is called by the scsi interrupt when * X* the transfer is complete. (or failed) * X\*******************************************************/ Xint cd_done(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct buf *bp; X int retval; X X if(scsi_debug & PRINTROUTINES) printf("cd_done%d ",unit); X if (! (xs->flags & INUSE)) /* paranoia always pays off */ X panic("scsi_xfer not in use!"); X if(bp = xs->bp) X { X switch(xs->error) X { X case XS_NOERROR: X bp->b_error = 0; X bp->b_resid = 0; X break; X X case XS_SENSE: X retval = (cd_interpret_sense(unit,xs)); X if(retval) X { X bp->b_flags |= B_ERROR; X bp->b_error = retval; X } X break; X X case XS_TIMEOUT: X printf("cd%d timeout\n",unit); X X case XS_BUSY: X /***********************************\ X * Just resubmit it straight back to * X * the SCSI driver to try it again * X \***********************************/ X if(xs->retries--) X { X xs->error = XS_NOERROR; X xs->flags &= ~ITSDONE; X if ( (*(cd_data[unit].sc_sw->scsi_cmd))(xs) X == SUCCESSFULLY_QUEUED) X { /* shhh! don't wake the job, ok? */ X /* don't tell cdstart either, */ X return; X } X /* xs->error is set by the scsi driver */ X } /* Fall through */ X X case XS_DRIVER_STUFFUP: X bp->b_flags |= B_ERROR; X bp->b_error = EIO; X break; X default: X printf("cd%d: unknown error category from scsi driver\n" X ,unit); X } X biodone(bp); X cd_free_xs(unit,xs,0); X cdstart(unit); /* If there's anything waiting.. do it */ X } X else /* special has finished */ X { X wakeup(xs); X } X} X/*******************************************************\ X* Perform special action on behalf of the user * X* Knows about the internals of this device * X\*******************************************************/ Xcdioctl(dev_t dev, int cmd, caddr_t addr, int flag) X{ X int error = 0; X unsigned int opri; X unsigned char unit, part; X register struct cd_data *cd; X X X /*******************************************************\ X * Find the device that the user is talking about * X \*******************************************************/ X unit = UNIT(dev); X part = PARTITION(dev); X cd = &cd_data[unit]; X if(scsi_debug & PRINTROUTINES) printf("cdioctl%d ",unit); X X /*******************************************************\ X * If the device is not valid.. abandon ship * X \*******************************************************/ X if (!(cd_data[unit].flags & CDVALID)) X return(EIO); X switch(cmd) X { X X case DIOCSBAD: X error = EINVAL; X break; X X case DIOCGDINFO: X *(struct disklabel *)addr = cd->disklabel; X break; X X case DIOCGPART: X ((struct partinfo *)addr)->disklab = &cd->disklabel; X ((struct partinfo *)addr)->part = X &cd->disklabel.d_partitions[PARTITION(dev)]; X break; X X case DIOCWDINFO: X case DIOCSDINFO: X if ((flag & FWRITE) == 0) X error = EBADF; X else X error = setdisklabel(&cd->disklabel, X (struct disklabel *)addr, X /*(cd->flags & DKFL_BSDLABEL) ? cd->openparts : */0, X 0); X if (error == 0) { X cd->flags |= CDHAVELABEL; X } X break; X X case DIOCWLABEL: X error = EBADF; X break; X X case CDIOCPLAYTRACKS: X { X struct ioc_play_track *args X = (struct ioc_play_track *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.sotc = 0; X data.page.audio.immed = 1; X if(error = cd_set_mode(unit,&data)) X break; X return(cd_play_tracks(unit X ,args->start_track X ,args->start_index X ,args->end_track X ,args->end_index X )); X } X break; X case CDIOCPLAYBLOCKS: X { X struct ioc_play_blocks *args X = (struct ioc_play_blocks *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.sotc = 0; X data.page.audio.immed = 1; X if(error = cd_set_mode(unit,&data)) X break; X return(cd_play(unit,args->blk,args->len)); X X X } X break; X case CDIOCREADSUBCHANNEL: X { X struct ioc_read_subchannel *args X = (struct ioc_read_subchannel *)addr; X struct cd_sub_channel_info data; X int len=args->data_len; X if(len>sizeof(data)|| X len<sizeof(struct cd_sub_channel_header)) { X error=EINVAL; X break; X } X if(error = cd_read_subchannel(unit,args->address_format, X args->data_format,args->track,&data,len)) { X break; X } X len=MIN(len,((data.header.data_len[0]<<8)+data.header.data_len[1]+ X sizeof(struct cd_sub_channel_header))); X if(copyout(&data,args->data,len)!=0) { X error=EFAULT; X } X } X break; X case CDIOREADTOCHEADER: X { X struct ioc_toc_header th; X if( error = cd_read_toc(unit,0,0,&th,sizeof(th))) X break; X th.len=(th.len&0xff)<<8+((th.len>>8)&0xff); X bcopy(&th,addr,sizeof(th)); X } X break; X case CDIOREADTOCENTRYS: X { X struct ioc_read_toc_entry *te= X (struct ioc_read_toc_entry *)addr; X struct cd_toc_entry data[65]; X struct ioc_toc_header *th; X int len=te->data_len; X th=(struct ioc_toc_header *)data; X X if(len>sizeof(data) || len<sizeof(struct cd_toc_entry)) { X error=EINVAL; X break; X } X if(error = cd_read_toc(unit,te->address_format, X te->starting_track, X data, X len)) X break; X len=MIN(len,((((th->len&0xff)<<8)+((th->len>>8)))+ X sizeof(*th))); X if(copyout(th,te->data,len)!=0) { X error=EFAULT; X } X X } X break; X case CDIOCSETPATCH: X { X struct ioc_patch *arg = (struct ioc_patch *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = arg->patch[0]; X data.page.audio.port[RIGHT_PORT].channels = arg->patch[1]; X data.page.audio.port[2].channels = arg->patch[2]; X data.page.audio.port[3].channels = arg->patch[3]; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCGETVOL: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X arg->vol[LEFT_PORT] = data.page.audio.port[LEFT_PORT].volume; X arg->vol[RIGHT_PORT] = data.page.audio.port[RIGHT_PORT].volume; X arg->vol[2] = data.page.audio.port[2].volume; X arg->vol[3] = data.page.audio.port[3].volume; X } X break; X case CDIOCSETVOL: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].volume = arg->vol[LEFT_PORT]; X data.page.audio.port[RIGHT_PORT].volume = arg->vol[RIGHT_PORT]; X data.page.audio.port[2].volume = arg->vol[2]; X data.page.audio.port[3].volume = arg->vol[3]; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCSETMONO: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL|RIGHT_CHANNEL|4|8; X data.page.audio.port[RIGHT_PORT].channels = LEFT_CHANNEL|RIGHT_CHANNEL; X data.page.audio.port[2].channels = 0; X data.page.audio.port[3].channels = 0; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCSETSTERIO: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = LEFT_CHANNEL; X data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; X data.page.audio.port[2].channels = 0; X data.page.audio.port[3].channels = 0; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCSETMUTE: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = 0; X data.page.audio.port[RIGHT_PORT].channels = 0; X data.page.audio.port[2].channels = 0; X data.page.audio.port[3].channels = 0; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCSETLEFT: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = 15; X data.page.audio.port[RIGHT_PORT].channels = 15; X data.page.audio.port[2].channels = 15; X data.page.audio.port[3].channels = 15; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCSETRIGHT: X { X struct ioc_vol *arg = (struct ioc_vol *)addr; X struct cd_mode_data data; X if(error = cd_get_mode(unit,&data,AUDIO_PAGE)) X break; X data.page.audio.port[LEFT_PORT].channels = RIGHT_CHANNEL; X data.page.audio.port[RIGHT_PORT].channels = RIGHT_CHANNEL; X data.page.audio.port[2].channels = 0; X data.page.audio.port[3].channels = 0; X if(error = cd_set_mode(unit,&data)) X break; X } X break; X case CDIOCRESUME: X error = cd_pause(unit,1); X break; X case CDIOCPAUSE: X error = cd_pause(unit,0); X break; X case CDIOCSTART: X error = cd_start_unit(unit,part,CD_START); X break; X case CDIOCSTOP: X error = cd_start_unit(unit,part,CD_STOP); X break; X case CDIOCEJECT: X error = cd_start_unit(unit,part,CD_EJECT); X break; X case CDIOCSETDEBUG: X scsi_debug = 0xfff; cd_debug = 0xfff; X break; X case CDIOCCLRDEBUG: X scsi_debug = 0; cd_debug = 0; X break; X case CDIOCRESET: X return(cd_reset(unit)); X break; X default: X error = ENOTTY; X break; X } X return (error); X} X X X/*******************************************************\ X* Load the label information on the named device * X* * X* EVENTUALLY take information about different * X* data tracks from the TOC and put it in the disklabel * X\*******************************************************/ Xint cdgetdisklabel(unit) Xunsigned char unit; X{ X /*unsigned int n, m;*/ X char *errstring; X struct dos_partition *dos_partition_p; X struct cd_data *cd = cd_data + unit; X X /*******************************************************\ X * If the inflo is already loaded, use it * X \*******************************************************/ X if(cd->flags & CDHAVELABEL) return; X X bzero(&cd->disklabel,sizeof(struct disklabel)); X /*******************************************************\ X * make partition 3 the whole disk in case of failure * X * then get pdinfo * X \*******************************************************/ X strncpy(cd->disklabel.d_typename,"scsi cd_rom",16); X strncpy(cd->disklabel.d_packname,"ficticious",16); X cd->disklabel.d_secsize = cd->params.blksize; /* as long as it's not 0 */ X cd->disklabel.d_nsectors = 100; X cd->disklabel.d_ntracks = 1; X cd->disklabel.d_ncylinders = (cd->params.disksize / 100) + 1; X cd->disklabel.d_secpercyl = 100; X cd->disklabel.d_secperunit = cd->params.disksize; X cd->disklabel.d_rpm = 300; X cd->disklabel.d_interleave = 1; X cd->disklabel.d_flags = D_REMOVABLE; X X cd->disklabel.d_npartitions = 1; X cd->disklabel.d_partitions[0].p_offset = 0; X cd->disklabel.d_partitions[0].p_size = cd->params.disksize; X cd->disklabel.d_partitions[0].p_fstype = 9; X X cd->disklabel.d_magic = DISKMAGIC; X cd->disklabel.d_magic2 = DISKMAGIC; X cd->disklabel.d_checksum = dkcksum(&(cd->disklabel)); X X /*******************************************************\ X * Signal to other users and routines that we now have a * X * disklabel that represents the media (maybe) * X \*******************************************************/ X cd->flags |= CDHAVELABEL; X return(ESUCCESS); X} X X/*******************************************************\ X* Find out form the device what it's capacity is * X\*******************************************************/ Xcd_size(unit, flags) X{ X struct scsi_read_cd_cap_data rdcap; X struct scsi_read_cd_capacity scsi_cmd; X int size; X int blksize; X X /*******************************************************\ X * make up a scsi command and ask the scsi driver to do * X * it for you. * X \*******************************************************/ X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = READ_CD_CAPACITY; X X /*******************************************************\ X * If the command works, interpret the result as a 4 byte* X * number of blocks * X \*******************************************************/ X if (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &rdcap, X sizeof(rdcap), X 2000, X flags) != 0) X { X printf("could not get size of unit %d\n", unit); X return(0); X } else { X size = rdcap.addr_0 + 1 ; X size += rdcap.addr_1 << 8; X size += rdcap.addr_2 << 16; X size += rdcap.addr_3 << 24; X blksize = rdcap.length_0 ; X blksize += rdcap.length_1 << 8; X blksize += rdcap.length_2 << 16; X blksize += rdcap.length_3 << 24; X } X if(cd_debug)printf("cd%d: %d %d byte blocks\n",unit,size,blksize); X cd_data[unit].params.disksize = size; X cd_data[unit].params.blksize = blksize; X return(size); X} X X/*******************************************************\ X* Check with the device that it is ok, (via scsi driver)* X\*******************************************************/ Xcd_req_sense(unit, flags) X{ X struct scsi_sense_data sense_data; 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_data); X X if (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X &sense_data, X sizeof(sense_data), X 2000, X flags) != 0) X { X return(ENXIO); X } X else X return(0); X} X X/*******************************************************\ X* Get the requested page into the buffer given * X\*******************************************************/ Xcd_get_mode(unit,data,page) Xint unit; Xstruct cd_mode_data *data; Xint page; X{ X struct scsi_mode_sense scsi_cmd; X int retval; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X bzero(data,sizeof(*data)); X scsi_cmd.op_code = MODE_SENSE; X scsi_cmd.page_code = page; X scsi_cmd.length = sizeof(*data) & 0xff; X retval = cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X data, X sizeof(*data), X 20000, /* should be immed */ X 0); X return (retval); X} X/*******************************************************\ X* Get the requested page into the buffer given * X\*******************************************************/ Xcd_set_mode(unit,data) Xint unit; Xstruct cd_mode_data *data; X{ X struct scsi_mode_select scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = MODE_SELECT; X scsi_cmd.pf = 1; X scsi_cmd.length = sizeof(*data) & 0xff; X data->header.data_length = 0; X /*show_mem(data,sizeof(*data));/**/ X return (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X data, X sizeof(*data), X 20000, /* should be immed */ X 0) X ); X} X/*******************************************************\ X* Get scsi driver to send a "start playing" command * X\*******************************************************/ Xcd_play(unit,blk,len) Xint unit,blk,len; X{ X struct scsi_play scsi_cmd; X int retval; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PLAY; X scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff; X scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff; X scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff; X scsi_cmd.blk_addr[3] = blk & 0xff; X scsi_cmd.xfer_len[0] = (len >> 8) & 0xff; X scsi_cmd.xfer_len[1] = len & 0xff; X retval = cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 200000, /* should be immed */ X 0); X return(retval); X} X/*******************************************************\ X* Get scsi driver to send a "start playing" command * X\*******************************************************/ Xcd_play_big(unit,blk,len) Xint unit,blk,len; X{ X struct scsi_play_big scsi_cmd; X int retval; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PLAY_BIG; X scsi_cmd.blk_addr[0] = (blk >> 24) & 0xff; X scsi_cmd.blk_addr[1] = (blk >> 16) & 0xff; X scsi_cmd.blk_addr[2] = (blk >> 8) & 0xff; X scsi_cmd.blk_addr[3] = blk & 0xff; X scsi_cmd.xfer_len[0] = (len >> 24) & 0xff; X scsi_cmd.xfer_len[1] = (len >> 16) & 0xff; X scsi_cmd.xfer_len[2] = (len >> 8) & 0xff; X scsi_cmd.xfer_len[3] = len & 0xff; X retval = cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 20000, /* should be immed */ X 0); X return(retval); X} X/*******************************************************\ X* Get scsi driver to send a "start playing" command * X\*******************************************************/ Xcd_play_tracks(unit,strack,sindex,etrack,eindex) Xint unit,strack,sindex,etrack,eindex; X{ X struct scsi_play_track scsi_cmd; X int retval; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PLAY_TRACK; X scsi_cmd.start_track = strack; X scsi_cmd.start_index = sindex; X scsi_cmd.end_track = etrack; X scsi_cmd.end_index = eindex; X retval = cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 20000, /* should be immed */ X 0); X return(retval); X} X/*******************************************************\ X* Get scsi driver to send a "start up" command * X\*******************************************************/ Xcd_pause(unit,go) Xint unit,go; X{ X struct scsi_pause scsi_cmd; X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PAUSE; X scsi_cmd.resume = go; X X return (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 2000, X 0)); X} X/*******************************************************\ X* Get scsi driver to send a "start up" command * X\*******************************************************/ Xcd_reset(unit) Xint unit; X{ X return(cd_scsi_cmd(unit,0,0,0,0,2000,SCSI_RESET)); X} X/*******************************************************\ X* Get scsi driver to send a "start up" command * X\*******************************************************/ Xcd_start_unit(unit,part,type) X{ X struct scsi_start_stop scsi_cmd; X X if(type==CD_EJECT && (cd_data[unit].openparts&~(1<<part)) == 0 ) { X cd_prevent_unit(unit,CD_EJECT,0); X } X X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = START_STOP; X scsi_cmd.start = type==CD_START?1:0; X scsi_cmd.loej = type==CD_EJECT?1:0; X X if (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(scsi_cmd), X 0, X 0, X 2000, X 0) != 0) { X return(ENXIO); X } else X return(0); X} X/*******************************************************\ X* Prevent or allow the user to remove the disk * X\*******************************************************/ Xcd_prevent_unit(unit,type,flags) Xint unit,type,flags; X{ X struct scsi_prevent scsi_cmd; X X if(type==CD_EJECT || type==PR_PREVENT || cd_data[unit].openparts == 0 ) { X bzero(&scsi_cmd, sizeof(scsi_cmd)); X scsi_cmd.op_code = PREVENT_ALLOW; X scsi_cmd.prevent=type==CD_EJECT?PR_ALLOW:type; X if (cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_prevent), X 0, X 0, X 5000, X 0) != 0) X { X if(!(flags & SCSI_SILENT)) X printf("cannot prevent/allow on cd%d\n", unit); X return(0); X } X } X return(1); X} X X/******************************************************\ X* Read Subchannel * X\******************************************************/ X Xcd_read_subchannel(unit,mode,format,track,data,len) Xint unit,mode,format,len; Xstruct cd_sub_channel_info *data; X{ X struct scsi_read_subchannel scsi_cmd; X int error; X X bzero(&scsi_cmd,sizeof(scsi_cmd)); X X scsi_cmd.op_code=READ_SUBCHANNEL; X if(mode==CD_MSF_FORMAT) X scsi_cmd.msf=1; X scsi_cmd.subQ=1; X scsi_cmd.subchan_format=format; X scsi_cmd.track=track; X scsi_cmd.data_len[0]=(len)>>8; X scsi_cmd.data_len[1]=(len)&0xff; X return cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_read_subchannel), X data, X len, X 5000, X 0); X} X X/*******************************************************\ X* Read Table of contents * X\*******************************************************/ Xcd_read_toc(unit,mode,start,data,len) Xint unit,mode,start,len; Xstruct cd_toc_entry *data; X{ X struct scsi_read_toc scsi_cmd; X int error; X int ntoc; X X bzero(&scsi_cmd,sizeof(scsi_cmd)); X /*if(len!=sizeof(struct ioc_toc_header)) X ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry); X else*/ X ntoc=len; X X scsi_cmd.op_code=READ_TOC; X if(mode==CD_MSF_FORMAT) X scsi_cmd.msf=1; X scsi_cmd.from_track=start; X scsi_cmd.data_len[0]=(ntoc)>>8; X scsi_cmd.data_len[1]=(ntoc)&0xff; X return cd_scsi_cmd(unit, X &scsi_cmd, X sizeof(struct scsi_read_toc), X data, X len, X 5000, X 0); X} X X X#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 ) X X/*******************************************************\ X* Get the scsi driver to send a full inquiry to the * X* device and use the results to fill out the disk * X* parameter structure. * X\*******************************************************/ X Xint cd_get_parms(unit, flags) X{ X struct cd_data *cd = cd_data + unit; X X /*******************************************************\ X * First check if we have it all loaded * X \*******************************************************/ X if(cd->flags & CDVALID) return(0); X /*******************************************************\ X * give a number of sectors so that sec * trks * cyls * X * is <= disk_size * X \*******************************************************/ X if(cd_size(unit, flags)) X { X cd->flags |= CDVALID; X return(0); X } X else X { X return(ENXIO); X } X} X X/*******************************************************\ X* close the device.. only called if we are the LAST * X* occurence of an open device * X\*******************************************************/ Xcdclose(dev) Xdev_t dev; X{ X unsigned char unit, part; X unsigned int old_priority; X X unit = UNIT(dev); X part = PARTITION(dev); X if(scsi_debug & TRACEOPENS) X printf("closing cd%d part %d\n",unit,part); X cd_data[unit].partflags[part] &= ~CDOPEN; X cd_data[unit].openparts &= ~(1 << part); X cd_prevent_unit(unit,PR_ALLOW,SCSI_SILENT); X return(0); 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 cd_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 struct cd_data *cd = cd_data + unit; X X if(scsi_debug & PRINTROUTINES) printf("\ncd_scsi_cmd%d ",unit); X if(cd->sc_sw) /* If we have a scsi driver */ X { X xs = cd_get_xs(unit,flags); /* should wait unless booting */ X if(!xs) X { X printf("cd_scsi_cmd%d: controller busy" X " (this should never happen)\n",unit); X return(EBUSY); X } X xs->flags |= INUSE; X /*******************************************************\ X * Fill out the scsi_xfer structure * X \*******************************************************/ X xs->flags |= flags; X xs->adapter = cd->ctlr; X xs->targ = cd->targ; X xs->lu = cd->lu; X xs->retries = CD_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 :cd_done; X xs->done_arg = unit; X xs->done_arg2 = (int)xs; Xretry: xs->error = XS_NOERROR; X xs->bp = 0; X retval = (*(cd->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 /*printf("err = %d ",xs->error);*/ X switch(xs->error) X { X case XS_NOERROR: X retval = ESUCCESS; X break; X case XS_SENSE: X retval = (cd_interpret_sense(unit,xs)); X break; X case XS_DRIVER_STUFFUP: X retval = EIO; X break; X X X case XS_BUSY: X case XS_TIMEOUT: 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("cd%d: unknown error category from scsi driver\n" X ,unit); X } X break; X case COMPLETE: X retval = ESUCCESS; X break; X case TRY_AGAIN_LATER: X if(xs->retries-- ) X { X if(tsleep( 0,PRIBIO + 2,"retry",hz * 2)) X { X xs->flags &= ~ITSDONE; X goto retry; X } X } X retval = EIO; X break; X default: X retval = EIO; X } X cd_free_xs(unit,xs,flags); X cdstart(unit); /* check if anything is waiting fr the xs */ X } X else X { X printf("cd%d: 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 cd_interpret_sense(unit,xs) Xint unit; Xstruct scsi_xfer *xs; X{ X struct scsi_sense_data *sense; X int key; X int silent; X X /***************************************************************\ X * If the flags say errs are ok, then always return ok. * X \***************************************************************/ X if (xs->flags & SCSI_ERR_OK) return(ESUCCESS); X silent = (xs->flags & SCSI_SILENT); X X sense = &(xs->sense); X switch(sense->error_class) X { X case 7: X { X key=sense->ext.extended.sense_key; X switch(key) X { X case 0x0: X return(ESUCCESS); X case 0x1: X if(!silent) X { X printf("cd%d: soft error(corrected) ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)", 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 printf("\n"); X } X return(ESUCCESS); X case 0x2: X if(!silent)printf("cd%d: not ready\n ", X unit); X return(ENODEV); X case 0x3: X if(!silent) X { X printf("cd%d: medium error ", unit); X if(sense->valid) X { X printf("block no. %d (decimal)", 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 printf("\n"); X } X return(EIO); X case 0x4: X if(!silent)printf("cd%d: non-media hardware failure\n ", X unit); X return(EIO); X case 0x5: X if(!silent)printf("cd%d: illegal request\n ", X unit); X return(EINVAL); X case 0x6: X if(!silent)printf("cd%d: Unit attention.\n ", unit); X if (cd_data[unit].openparts) X cd_data[unit].flags &= ~(CDVALID | CDHAVELABEL); X { X return(EIO); X } X return(ESUCCESS); X case 0x7: X if(!silent) X { X printf("cd%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 printf("\n"); X } X return(EACCES); X case 0x8: X if(!silent) X { X printf("cd%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 printf("\n"); X } X return(EIO); X case 0x9: X if(!silent)printf("cd%d: vendor unique\n", X unit); X return(EIO); X case 0xa: X if(!silent)printf("cd%d: copy aborted\n ", X unit); X return(EIO); X case 0xb: X if(!silent)printf("cd%d: command aborted\n ", X unit); X return(EIO); X case 0xc: X if(!silent) X { X printf("cd%d: search returned\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 printf("\n"); X } X return(ESUCCESS); X case 0xd: X if(!silent)printf("cd%d: volume overflow\n ", X unit); X return(ENOSPC); X case 0xe: X if(!silent) X { X printf("cd%d: verify miscompare\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 printf("\n"); X } X return(EIO); X case 0xf: X if(!silent)printf("cd%d: unknown error key\n ", X unit); X return(EIO); X } X break; 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("cd%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 X Xint Xcdsize(dev_t dev) X{ X return (-1); X} X Xshow_mem(address,num) Xunsigned char *address; Xint num; X{ X int x,y; X printf("------------------------------"); X for (y = 0; y<num; y += 1) X { X if(!(y % 16)) X printf("\n%03d: ",y); X printf("%02x ",*address++); X } X printf("\n------------------------------\n"); X} X END-of-scsi/cd.c exit