* Written by Julian Elischer (julian@tfs.com)
* for TRW Financial Systems for use under the MACH(2.5) operating system.
* TRW Financial Systems, in accordance with their agreement with Carnegie
* Mellon University, makes this software available to CMU to distribute
* or use in any manner that they see fit as long as this message is kept with
* the software. For this reason TFS also grants any other persons or
* organisations permission to use or modify this software.
* TFS supplies this software to be publicly redistributed
* on the understanding that TFS is not responsible for the correct
* functioning of this software in any circumstances.
* Ported to run under 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
* $Id: cd.c,v 1.10 1993/09/21 05:30:35 rgrimes Exp $
#include <sys/disklabel.h>
#include <scsi/scsi_all.h>
#include <scsi/scsi_cd.h>
#include <scsi/scsi_disk.h> /* rw_big and start_stop come from there */
#include <scsi/scsiconf.h>
long int cdstrats
,cdqueues
;
#define SECSIZE 2048 /* XXX */ /* default only */
#define PARTITION(z) (minor(z) & 0x07)
#define UNIT(z) ( (minor(z) >> UNITSHIFT) )
#define CDVALID 0x02 /* PARAMS LOADED */
#define CDINIT 0x04 /* device has been init'd */
#define CDWAIT 0x08 /* device has someone waiting */
#define CDHAVELABEL 0x10 /* have read the label */
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 */
int cmdscount
; /* cmds allowed outstanding by board*/
u_long disksize
; /* total number sectors */
struct disklabel disklabel
;
int partflags
[MAXPARTITIONS
]; /* per partition flags */
int openparts
; /* one bit for each open partition */
struct scsi_xfer
*free_xfer
;
struct scsi_xfer scsi_xfer
[CDOUTSTANDING
]; /* XXX */
struct cd_data
**cd_data
;
static int next_cd_unit
= 0;
/***********************************************************************\
* The routine called by the low level scsi routine when it discovers *
* A device suitable for this driver *
\***********************************************************************/
int cdattach(ctlr
,targ
,lu
,scsi_switch
)
struct scsi_switch
*scsi_switch
;
struct cd_data
*cd
, **cdrealloc
;
if(scsi_debug
& PRINTROUTINES
) printf("cdattach: ");
/*******************************************************\
* Check if we have resources allocated yet, if not *
* allocate and initialize them *
\*******************************************************/
malloc(sizeof(struct cd_driver
),M_DEVBUF
,M_NOWAIT
);
printf("cd%d: malloc failed for cd_driver\n",unit
);
bzero(cd_driver
,sizeof(cd_driver
));
/*******************************************************\
* allocate the resources for another drive *
* if we have already allocate a cd_data pointer we must *
* copy the old pointers into a new region that is *
* larger and release the old region, aka realloc *
\*******************************************************/
* This if will always be true for now, but future code may
* preallocate more units to reduce overhead. This would be
* done by changing the malloc to be (next_cd_unit * x) and
* the cd_driver->size++ to be +x
if(unit
>= cd_driver
->size
)
malloc(sizeof(cd_driver
->cd_data
) * next_cd_unit
,
printf("cd%d: malloc failed for cdrealloc\n",unit
);
/* Make sure we have something to copy before we copy it */
bzero(cdrealloc
,sizeof(cd_driver
->cd_data
) * next_cd_unit
);
bcopy(cd_driver
->cd_data
,cdrealloc
,
sizeof(cd_driver
->cd_data
) * cd_driver
->size
);
free(cd_driver
->cd_data
,M_DEVBUF
);
cd_driver
->cd_data
= cdrealloc
;
cd_driver
->cd_data
[unit
] = NULL
;
if(cd_driver
->cd_data
[unit
])
printf("cd%d: Already has storage!\n",unit
);
/*******************************************************\
* allocate the per drive data area *
\*******************************************************/
cd
= cd_driver
->cd_data
[unit
] =
malloc(sizeof(struct cd_data
),M_DEVBUF
,M_NOWAIT
);
printf("cd%d: malloc failed for cd_data\n",unit
);
bzero(cd
,sizeof(struct cd_data
));
/*******************************************************\
* Store information needed to contact our base driver *
\*******************************************************/
cd
->cmdscount
= CDOUTSTANDING
; /* XXX (ask the board) */
cd
->scsi_xfer
[i
].next
= cd
->free_xfer
;
cd
->free_xfer
= &cd
->scsi_xfer
[i
];
/*******************************************************\
* Use the subdriver to request information regarding *
* the drive. We cannot use interrupts yet, so the *
* request must specify this. *
\*******************************************************/
cd_get_parms(unit
, SCSI_NOSLEEP
| SCSI_NOMASK
);
printf("cd%d: cd present.[%d x %d byte records]\n",
printf("cd%d: drive empty\n", unit
);
/*******************************************************\
* open the device. Make sure the partition info *
* is a up-to-date as can be. *
\*******************************************************/
struct cd_parms cd_parms
;
if(scsi_debug
& (PRINTROUTINES
| TRACEOPENS
))
printf("cdopen: dev=0x%x (unit %d (of %d),partition %d)\n"
,dev
,unit
,cd_driver
->size
,part
);
/*******************************************************\
* Check the unit is legal *
\*******************************************************/
if ( unit
>= cd_driver
->size
)
cd
= cd_driver
->cd_data
[unit
];
/*******************************************************\
* Make sure the device has been initialised *
\*******************************************************/
if ((cd
== NULL
) || (!(cd
->flags
& CDINIT
)))
/*******************************************************\
* If it's been invalidated, and not everybody has *
* closed it then forbid re-entry. *
* (may have changed media) *
\*******************************************************/
if ((! (cd
->flags
& CDVALID
))
/*******************************************************\
* Check that it is still responding and ok. *
* if the media has been changed this will result in a *
* "unit attention" error which the error code will *
* disregard because the CDVALID flag is not yet set *
\*******************************************************/
cd_test_ready(unit
, SCSI_SILENT
);
/*******************************************************\
* Next time actually take notice of error returns *
\*******************************************************/
if (cd_test_ready(unit
, SCSI_SILENT
) != 0) {
if(scsi_debug
& TRACEOPENS
)
if(scsi_debug
& TRACEOPENS
)
printf("Device present\n");
/*******************************************************\
* In case it is a funny one, tell it to start *
* not needed for some drives *
\*******************************************************/
cd_start_unit(unit
,part
,CD_START
);
cd_prevent_unit(unit
,PR_PREVENT
,SCSI_SILENT
);
if(scsi_debug
& TRACEOPENS
)
/*******************************************************\
* Load the physical device parameters *
\*******************************************************/
if(scsi_debug
& TRACEOPENS
)
printf("Params loaded ");
/*******************************************************\
* Load the partition info if not already loaded *
\*******************************************************/
if(scsi_debug
& TRACEOPENS
)
printf("Disklabel fabricated ");
/*******************************************************\
* Check the partition is legal *
\*******************************************************/
if (( part
>= cd
->disklabel
.d_npartitions
)
if(scsi_debug
& TRACEOPENS
)
printf("partition %d > %d\n",part
,cd
->disklabel
.d_npartitions
);
cd_prevent_unit(unit
,PR_ALLOW
,SCSI_SILENT
);
/*******************************************************\
* Check that the partition exists *
\*******************************************************/
if (( cd
->disklabel
.d_partitions
[part
].p_fstype
!= FS_UNUSED
)
cd
->partflags
[part
] |= CDOPEN
;
cd
->openparts
|= (1 << part
);
if(scsi_debug
& TRACEOPENS
)
printf("open complete\n");
if(scsi_debug
& TRACEOPENS
)
printf("part %d type UNUSED\n",part
);
cd_prevent_unit(unit
,PR_ALLOW
,SCSI_SILENT
);
/*******************************************************\
* Get ownership of a scsi_xfer structure *
* If need be, sleep on it, until it comes free *
\*******************************************************/
struct scsi_xfer
*cd_get_xs(unit
,flags
)
cd
= cd_driver
->cd_data
[unit
];
if(flags
& (SCSI_NOSLEEP
| SCSI_NOMASK
))
cd
->free_xfer
= xs
->next
;
while (!(xs
= cd
->free_xfer
))
cd
->xfer_block_wait
++; /* someone waiting! */
sleep((caddr_t
)&cd
->free_xfer
, PRIBIO
+1);
cd
->free_xfer
= xs
->next
;
/*******************************************************\
* Free a scsi_xfer, wake processes waiting for it *
\*******************************************************/
cd_free_xs(unit
,xs
,flags
)
cd
= cd_driver
->cd_data
[unit
];
printf("cd%d: doing a wakeup from NOMASK mode\n", unit
);
wakeup((caddr_t
)&cd
->free_xfer
);
xs
->next
= cd
->free_xfer
;
wakeup((caddr_t
)&cd
->free_xfer
);
xs
->next
= cd
->free_xfer
;
/*******************************************************\
* trim the size of the transfer if needed, *
* basically the smaller of our max and the scsi driver's*
* minphys (note we have no max ourselves) *
\*******************************************************/
/* Trim buffer length if buffer-size is bigger than page size */
(*(cd_driver
->cd_data
[UNIT(bp
->b_dev
)]->sc_sw
->scsi_minphys
))(bp
);
/*******************************************************\
* Actually translate the requested transfer into *
* one the physical driver can understand *
* The transfer is described by a buf and will include *
* only one physical transfer. *
\*******************************************************/
unit
= UNIT((bp
->b_dev
));
cd
= cd_driver
->cd_data
[unit
];
if(scsi_debug
& PRINTROUTINES
) printf("\ncdstrategy ");
if(scsi_debug
& SHOWREQUESTS
) printf("cd%d: %d bytes @ blk%d\n",
unit
,bp
->b_bcount
,bp
->b_blkno
);
/*******************************************************\
* If the device has been made invalid, error out *
* maybe the media changed *
\*******************************************************/
if(!(cd
->flags
& CDVALID
))
/*******************************************************\
* can't ever write to a CD *
\*******************************************************/
if ((bp
->b_flags
& B_READ
) == 0) {
/*******************************************************\
* If it's a null transfer, return immediatly *
\*******************************************************/
/*******************************************************\
* Decide which unit and partition we are talking about *
\*******************************************************/
if(PARTITION(bp
->b_dev
) != RAW_PART
)
if (!(cd
->flags
& CDHAVELABEL
))
* do bounds checking, adjust transfer. if error, process.
* if end of partition, just return
if (bounds_check_with_label(bp
,&cd
->disklabel
,1) <= 0)
/* otherwise, process transfer request */
/*******************************************************\
* Place it in the queue of disk activities for this disk*
\*******************************************************/
/*******************************************************\
* Tell the device to get going on the transfer if it's *
* not doing anything, otherwise just wait for completion*
\*******************************************************/
/*******************************************************\
* Correctly set the buf to indicate a completed xfer *
\*******************************************************/
bp
->b_resid
= bp
->b_bcount
;
/***************************************************************\
* cdstart looks to see if there is a buf waiting for the device *
* and that the device is not already busy. If both are true, *
* It deques the buf and creates a scsi command to perform the *
* transfer in the buf. The transfer request will call cd_done *
* on completion, which will in turn call this routine again *
* so that the next queued transfer is performed. *
* The bufs are queued by the strategy routine (cdstrategy) *
* 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. *
* must be called at the correct (highish) spl level *
\***************************************************************/
/* cdstart() is called at SPLCD from cdstrategy and cd_done*/
register struct buf
*bp
= 0;
if(scsi_debug
& PRINTROUTINES
) printf("cdstart%d ",unit
);
cd
= cd_driver
->cd_data
[unit
];
/*******************************************************\
* See if there is a buf to do and we are not already *
\*******************************************************/
return; /* none for us, unit already underway */
if(cd
->xfer_block_wait
) /* there is one, but a special waits */
return; /* give the special that's waiting a chance to run */
if ((bp
= dp
->b_actf
) != NULL
) /* yes, an assign */
dp
->b_actf
= bp
->av_forw
;
xs
=cd_get_xs(unit
,0); /* ok we can grab it */
xs
->flags
= INUSE
; /* Now ours */
/***************************************************************\
* Should reject all queued entries if CDVALID is not true *
\***************************************************************/
if(!(cd
->flags
& CDVALID
))
goto bad
; /* no I/O.. media changed or something */
/*******************************************************\
* We have a buf, now we should move the data into *
* a scsi_xfer definition and try start it *
* First, translate the block to absolute *
* and put it in terms of the logical blocksize of the *
* really a bit silly until we have real partitions, but.*
\*******************************************************/
blkno
= bp
->b_blkno
/ (cd
->params
.blksize
/512);
if(PARTITION(bp
->b_dev
) != RAW_PART
)
p
= cd
->disklabel
.d_partitions
+ PARTITION(bp
->b_dev
);
nblk
= (bp
->b_bcount
+ (cd
->params
.blksize
- 1)) / (cd
->params
.blksize
);
/* what if something asks for 512 bytes not on a 2k boundary? *//*XXX*/
/*******************************************************\
* Fill out the scsi command *
\*******************************************************/
bzero(&cmd
, sizeof(cmd
));
cmd
.addr_3
= (blkno
& 0xff000000) >> 24;
cmd
.addr_2
= (blkno
& 0xff0000) >> 16;
cmd
.addr_1
= (blkno
& 0xff00) >> 8;
cmd
.addr_0
= blkno
& 0xff;
cmd
.length2
= (nblk
& 0xff00) >> 8;
cmd
.length1
= (nblk
& 0xff);
/*******************************************************\
* Fill out the scsi_xfer structure *
* Note: we cannot sleep as we may be an interrupt *
\*******************************************************/
xs
->flags
|= SCSI_NOSLEEP
;
xs
->retries
= CD_RETRIES
;
xs
->timeout
= 10000;/* 10000 millisecs for a disk !*/
xs
->cmd
= (struct scsi_generic
*)&cmd
;
xs
->cmdlen
= sizeof(cmd
);
xs
->resid
= bp
->b_bcount
;
xs
->data
= (u_char
*)bp
->b_un
.b_addr
;
xs
->datalen
= bp
->b_bcount
;
/*******************************************************\
* Pass all this info to the scsi driver. *
\*******************************************************/
if ( (*(cd
->sc_sw
->scsi_cmd
))(xs
) != SUCCESSFULLY_QUEUED
)
printf("cd%d: oops not queued",unit
);
bad
: xs
->error
= XS_DRIVER_STUFFUP
;
/*******************************************************\
* This routine is called by the scsi interrupt when *
* the transfer is complete. (or failed) *
\*******************************************************/
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
if(scsi_debug
& PRINTROUTINES
) printf("cd_done%d ",unit
);
if (! (xs
->flags
& INUSE
)) /* paranoia always pays off */
panic("scsi_xfer not in use!");
retval
= (cd_interpret_sense(unit
,xs
));
printf("cd%d timeout\n",unit
);
/***********************************\
* Just resubmit it straight back to *
* the SCSI driver to try it again *
\***********************************/
if ((*(cd
->sc_sw
->scsi_cmd
))(xs
)
{ /* shhh! don't wake the job, ok? */
/* don't tell cdstart either, */
/* xs->error is set by the scsi driver */
printf("cd%d: unknown error category from scsi driver\n"
cdstart(unit
); /* If there's anything waiting.. do it */
/*******************************************************\
* Perform special action on behalf of the user *
* Knows about the internals of this device *
\*******************************************************/
cdioctl(dev_t dev
, int cmd
, caddr_t addr
, int flag
)
unsigned char unit
, part
;
register struct cd_data
*cd
;
/*******************************************************\
* Find the device that the user is talking about *
\*******************************************************/
cd
= cd_driver
->cd_data
[unit
];
if(scsi_debug
& PRINTROUTINES
) printf("cdioctl%d ",unit
);
/*******************************************************\
* If the device is not valid.. abandon ship *
\*******************************************************/
if (!(cd_driver
->cd_data
[unit
]->flags
& CDVALID
))
*(struct disklabel
*)addr
= cd
->disklabel
;
((struct partinfo
*)addr
)->disklab
= &cd
->disklabel
;
((struct partinfo
*)addr
)->part
=
&cd
->disklabel
.d_partitions
[PARTITION(dev
)];
if ((flag
& FWRITE
) == 0)
error
= setdisklabel(&cd
->disklabel
,
(struct disklabel
*)addr
,
/*(cd->flags & DKFL_BSDLABEL) ? cd->openparts : */0,
cd
->flags
|= CDHAVELABEL
;
struct ioc_play_track
*args
= (struct ioc_play_track
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.flags
&= ~CD_PA_SOTC
;
data
.page
.audio
.flags
|= CD_PA_IMMED
;
if(error
= cd_set_mode(unit
,&data
))
return(cd_play_tracks(unit
struct ioc_play_msf
*args
= (struct ioc_play_msf
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.flags
&= ~CD_PA_SOTC
;
data
.page
.audio
.flags
|= CD_PA_IMMED
;
if(error
= cd_set_mode(unit
,&data
))
struct ioc_play_blocks
*args
= (struct ioc_play_blocks
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.flags
&= ~CD_PA_SOTC
;
data
.page
.audio
.flags
|= CD_PA_IMMED
;
if(error
= cd_set_mode(unit
,&data
))
return(cd_play(unit
,args
->blk
,args
->len
));
case CDIOCREADSUBCHANNEL
:
struct ioc_read_subchannel
*args
= (struct ioc_read_subchannel
*)addr
;
struct cd_sub_channel_info data
;
len
<sizeof(struct cd_sub_channel_header
)) {
if(error
= cd_read_subchannel(unit
,args
->address_format
,
args
->data_format
,args
->track
,&data
,len
)) {
len
=MIN(len
,((data
.header
.data_len
[0]<<8)+data
.header
.data_len
[1]+
sizeof(struct cd_sub_channel_header
)));
if(copyout(&data
,args
->data
,len
)!=0) {
struct ioc_toc_header th
;
if( error
= cd_read_toc(unit
,0,0,&th
,sizeof(th
)))
th
.len
=(th
.len
&0xff)<<8+((th
.len
>>8)&0xff);
bcopy(&th
,addr
,sizeof(th
));
struct ioc_read_toc_entry
*te
=
(struct ioc_read_toc_entry
*)addr
;
struct cd_toc_entry data
[65];
struct ioc_toc_header
*th
;
th
=(struct ioc_toc_header
*)data
;
if(len
>sizeof(data
) || len
<sizeof(struct cd_toc_entry
)) {
if(error
= cd_read_toc(unit
,te
->address_format
,
len
=MIN(len
,((((th
->len
&0xff)<<8)+((th
->len
>>8)))+
if(copyout(th
,te
->data
,len
)!=0) {
struct ioc_patch
*arg
= (struct ioc_patch
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= arg
->patch
[0];
data
.page
.audio
.port
[RIGHT_PORT
].channels
= arg
->patch
[1];
data
.page
.audio
.port
[2].channels
= arg
->patch
[2];
data
.page
.audio
.port
[3].channels
= arg
->patch
[3];
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
arg
->vol
[LEFT_PORT
] = data
.page
.audio
.port
[LEFT_PORT
].volume
;
arg
->vol
[RIGHT_PORT
] = data
.page
.audio
.port
[RIGHT_PORT
].volume
;
arg
->vol
[2] = data
.page
.audio
.port
[2].volume
;
arg
->vol
[3] = data
.page
.audio
.port
[3].volume
;
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].volume
= arg
->vol
[LEFT_PORT
];
data
.page
.audio
.port
[RIGHT_PORT
].volume
= arg
->vol
[RIGHT_PORT
];
data
.page
.audio
.port
[2].volume
= arg
->vol
[2];
data
.page
.audio
.port
[3].volume
= arg
->vol
[3];
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= LEFT_CHANNEL
|RIGHT_CHANNEL
|4|8;
data
.page
.audio
.port
[RIGHT_PORT
].channels
= LEFT_CHANNEL
|RIGHT_CHANNEL
;
data
.page
.audio
.port
[2].channels
= 0;
data
.page
.audio
.port
[3].channels
= 0;
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= LEFT_CHANNEL
;
data
.page
.audio
.port
[RIGHT_PORT
].channels
= RIGHT_CHANNEL
;
data
.page
.audio
.port
[2].channels
= 0;
data
.page
.audio
.port
[3].channels
= 0;
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= 0;
data
.page
.audio
.port
[RIGHT_PORT
].channels
= 0;
data
.page
.audio
.port
[2].channels
= 0;
data
.page
.audio
.port
[3].channels
= 0;
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= LEFT_CHANNEL
;
data
.page
.audio
.port
[RIGHT_PORT
].channels
= LEFT_CHANNEL
;
data
.page
.audio
.port
[2].channels
= 0;
data
.page
.audio
.port
[3].channels
= 0;
if(error
= cd_set_mode(unit
,&data
))
struct ioc_vol
*arg
= (struct ioc_vol
*)addr
;
struct cd_mode_data data
;
if(error
= cd_get_mode(unit
,&data
,AUDIO_PAGE
))
data
.page
.audio
.port
[LEFT_PORT
].channels
= RIGHT_CHANNEL
;
data
.page
.audio
.port
[RIGHT_PORT
].channels
= RIGHT_CHANNEL
;
data
.page
.audio
.port
[2].channels
= 0;
data
.page
.audio
.port
[3].channels
= 0;
if(error
= cd_set_mode(unit
,&data
))
error
= cd_pause(unit
,1);
error
= cd_pause(unit
,0);
error
= cd_start_unit(unit
,part
,CD_START
);
error
= cd_start_unit(unit
,part
,CD_STOP
);
error
= cd_start_unit(unit
,part
,CD_EJECT
);
scsi_debug
= 0xfff; cd_debug
= 0xfff;
scsi_debug
= 0; cd_debug
= 0;
/*******************************************************\
* Load the label information on the named device *
* EVENTUALLY take information about different *
* data tracks from the TOC and put it in the disklabel *
\*******************************************************/
struct dos_partition
*dos_partition_p
;
cd
= cd_driver
->cd_data
[unit
];
/*******************************************************\
* If the info is already loaded, use it *
\*******************************************************/
if(cd
->flags
& CDHAVELABEL
) return;
bzero(&cd
->disklabel
,sizeof(struct disklabel
));
/*******************************************************\
* make partition 3 the whole disk in case of failure *
\*******************************************************/
strncpy(cd
->disklabel
.d_typename
,"scsi cd_rom",16);
strncpy(cd
->disklabel
.d_packname
,"ficticious",16);
cd
->disklabel
.d_secsize
= cd
->params
.blksize
; /* as long as it's not 0 */
cd
->disklabel
.d_nsectors
= 100;
cd
->disklabel
.d_ntracks
= 1;
cd
->disklabel
.d_ncylinders
= (cd
->params
.disksize
/ 100) + 1;
cd
->disklabel
.d_secpercyl
= 100;
cd
->disklabel
.d_secperunit
= cd
->params
.disksize
;
cd
->disklabel
.d_rpm
= 300;
cd
->disklabel
.d_interleave
= 1;
cd
->disklabel
.d_flags
= D_REMOVABLE
;
cd
->disklabel
.d_npartitions
= 1;
cd
->disklabel
.d_partitions
[0].p_offset
= 0;
cd
->disklabel
.d_partitions
[0].p_size
= cd
->params
.disksize
* (cd
->params
.blksize
/ 512);
cd
->disklabel
.d_partitions
[0].p_fstype
= 9;
cd
->disklabel
.d_magic
= DISKMAGIC
;
cd
->disklabel
.d_magic2
= DISKMAGIC
;
cd
->disklabel
.d_checksum
= dkcksum(&(cd
->disklabel
));
/*******************************************************\
* Signal to other users and routines that we now have a *
* disklabel that represents the media (maybe) *
\*******************************************************/
cd
->flags
|= CDHAVELABEL
;
/*******************************************************\
* Get scsi driver to send a "are you ready" command *
\*******************************************************/
cd_test_ready(unit
,flags
)
struct scsi_test_unit_ready scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= TEST_UNIT_READY
;
return (cd_scsi_cmd(unit
,
/*******************************************************\
* Find out form the device what it's capacity is *
\*******************************************************/
struct scsi_read_cd_cap_data rdcap
;
struct scsi_read_cd_capacity scsi_cmd
;
/*******************************************************\
* make up a scsi command and ask the scsi driver to do *
\*******************************************************/
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= READ_CD_CAPACITY
;
/*******************************************************\
* If the command works, interpret the result as a 4 byte*
\*******************************************************/
printf("cd%d: could not get size\n", unit
);
size
= rdcap
.addr_0
+ 1 ;
size
+= rdcap
.addr_1
<< 8;
size
+= rdcap
.addr_2
<< 16;
size
+= rdcap
.addr_3
<< 24;
blksize
= rdcap
.length_0
;
blksize
+= rdcap
.length_1
<< 8;
blksize
+= rdcap
.length_2
<< 16;
blksize
+= rdcap
.length_3
<< 24;
if(cd_debug
)printf("cd%d: %d %d byte blocks\n",unit
,size
,blksize
);
cd_driver
->cd_data
[unit
]->params
.disksize
= size
;
cd_driver
->cd_data
[unit
]->params
.blksize
= blksize
;
/*******************************************************\
* Get the requested page into the buffer given *
\*******************************************************/
cd_get_mode(unit
,data
,page
)
struct cd_mode_data
*data
;
struct scsi_mode_sense scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
bzero(data
,sizeof(*data
));
scsi_cmd
.op_code
= MODE_SENSE
;
scsi_cmd
.length
= sizeof(*data
) & 0xff;
retval
= cd_scsi_cmd(unit
,
20000, /* should be immed */
/*******************************************************\
* Get the requested page into the buffer given *
\*******************************************************/
struct cd_mode_data
*data
;
struct scsi_mode_select scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= MODE_SELECT
;
scsi_cmd
.byte2
|= SMS_PF
;
scsi_cmd
.length
= sizeof(*data
) & 0xff;
data
->header
.data_length
= 0;
/*show_mem(data,sizeof(*data));*/
return (cd_scsi_cmd(unit
,
20000, /* should be immed */
/*******************************************************\
* Get scsi driver to send a "start playing" command *
\*******************************************************/
struct scsi_play scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.blk_addr
[0] = (blk
>> 24) & 0xff;
scsi_cmd
.blk_addr
[1] = (blk
>> 16) & 0xff;
scsi_cmd
.blk_addr
[2] = (blk
>> 8) & 0xff;
scsi_cmd
.blk_addr
[3] = blk
& 0xff;
scsi_cmd
.xfer_len
[0] = (len
>> 8) & 0xff;
scsi_cmd
.xfer_len
[1] = len
& 0xff;
retval
= cd_scsi_cmd(unit
,
200000, /* should be immed */
/*******************************************************\
* Get scsi driver to send a "start playing" command *
\*******************************************************/
cd_play_big(unit
,blk
,len
)
struct scsi_play_big scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= PLAY_BIG
;
scsi_cmd
.blk_addr
[0] = (blk
>> 24) & 0xff;
scsi_cmd
.blk_addr
[1] = (blk
>> 16) & 0xff;
scsi_cmd
.blk_addr
[2] = (blk
>> 8) & 0xff;
scsi_cmd
.blk_addr
[3] = blk
& 0xff;
scsi_cmd
.xfer_len
[0] = (len
>> 24) & 0xff;
scsi_cmd
.xfer_len
[1] = (len
>> 16) & 0xff;
scsi_cmd
.xfer_len
[2] = (len
>> 8) & 0xff;
scsi_cmd
.xfer_len
[3] = len
& 0xff;
retval
= cd_scsi_cmd(unit
,
20000, /* should be immed */
/*******************************************************\
* Get scsi driver to send a "start playing" command *
\*******************************************************/
cd_play_tracks(unit
,strack
,sindex
,etrack
,eindex
)
int unit
,strack
,sindex
,etrack
,eindex
;
struct scsi_play_track scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= PLAY_TRACK
;
scsi_cmd
.start_track
= strack
;
scsi_cmd
.start_index
= sindex
;
scsi_cmd
.end_track
= etrack
;
scsi_cmd
.end_index
= eindex
;
retval
= cd_scsi_cmd(unit
,
20000, /* should be immed */
/*******************************************************\
* Get scsi driver to send a "play msf" command *
\*******************************************************/
cd_play_msf(unit
,startm
,starts
,startf
,endm
,ends
,endf
)
int unit
,startm
,starts
,startf
,endm
,ends
,endf
;
struct scsi_play_msf scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= PLAY_MSF
;
return (cd_scsi_cmd(unit
,
/*******************************************************\
* Get scsi driver to send a "start up" command *
\*******************************************************/
struct scsi_pause scsi_cmd
;
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= PAUSE
;
return (cd_scsi_cmd(unit
,
/*******************************************************\
* Get scsi driver to send a "RESET" command *
\*******************************************************/
/*******************************************************\
* Get scsi driver to send a "start up" command *
\*******************************************************/
cd_start_unit(unit
,part
,type
)
struct scsi_start_stop scsi_cmd
;
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
/*&& (cd->openparts == 0)*/)/* trouble is WE have it open *//*XXX*/
cd_prevent_unit(unit
,PR_ALLOW
,0);
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= START_STOP
;
scsi_cmd
.how
|= (type
==CD_START
)?SSS_START
:0;
scsi_cmd
.how
|= (type
==CD_EJECT
)?SSS_LOEJ
:0;
/*******************************************************\
* Prevent or allow the user to remove the disk *
\*******************************************************/
cd_prevent_unit(unit
,type
,flags
)
struct scsi_prevent scsi_cmd
;
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
|| ( type
==PR_ALLOW
&& cd
->openparts
== 0 ))/*XXX*/
bzero(&scsi_cmd
, sizeof(scsi_cmd
));
scsi_cmd
.op_code
= PREVENT_ALLOW
;
sizeof(struct scsi_prevent
),
if(!(flags
& SCSI_SILENT
))
printf("cd%d: cannot prevent/allow\n", unit
);
/******************************************************\
\******************************************************/
cd_read_subchannel(unit
,mode
,format
,track
,data
,len
)
int unit
,mode
,format
,len
;
struct cd_sub_channel_info
*data
;
struct scsi_read_subchannel scsi_cmd
;
bzero(&scsi_cmd
,sizeof(scsi_cmd
));
scsi_cmd
.op_code
=READ_SUBCHANNEL
;
scsi_cmd
.byte2
|= CD_MSF
;
scsi_cmd
.subchan_format
=format
;
scsi_cmd
.data_len
[0]=(len
)>>8;
scsi_cmd
.data_len
[1]=(len
)&0xff;
sizeof(struct scsi_read_subchannel
),
/*******************************************************\
* Read Table of contents *
\*******************************************************/
cd_read_toc(unit
,mode
,start
,data
,len
)
struct cd_toc_entry
*data
;
struct scsi_read_toc scsi_cmd
;
bzero(&scsi_cmd
,sizeof(scsi_cmd
));
/*if(len!=sizeof(struct ioc_toc_header))
ntoc=((len)-sizeof(struct ioc_toc_header))/sizeof(struct cd_toc_entry);
scsi_cmd
.op_code
=READ_TOC
;
scsi_cmd
.byte2
|= CD_MSF
;
scsi_cmd
.from_track
=start
;
scsi_cmd
.data_len
[0]=(ntoc
)>>8;
scsi_cmd
.data_len
[1]=(ntoc
)&0xff;
sizeof(struct scsi_read_toc
),
#define b2tol(a) (((unsigned)(a##_1) << 8) + (unsigned)a##_0 )
/*******************************************************\
* Get the scsi driver to send a full inquiry to the *
* device and use the results to fill out the disk *
\*******************************************************/
int cd_get_parms(unit
, flags
)
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
/*******************************************************\
* First check if we have it all loaded *
\*******************************************************/
if(cd
->flags
& CDVALID
) return(0);
/*******************************************************\
* give a number of sectors so that sec * trks * cyls *
\*******************************************************/
/*******************************************************\
* close the device.. only called if we are the LAST *
* occurence of an open device *
\*******************************************************/
unsigned char unit
, part
;
unsigned int old_priority
;
if(scsi_debug
& TRACEOPENS
)
printf("cd%d: closing part %d\n",unit
,part
);
cd_driver
->cd_data
[unit
]->partflags
[part
] &= ~CDOPEN
;
cd_driver
->cd_data
[unit
]->openparts
&= ~(1 << part
);
cd_prevent_unit(unit
,PR_ALLOW
,SCSI_SILENT
);
/*******************************************************\
* ask the scsi driver to perform a command for us. *
* Call it through the switch table, and tell it which *
* sub-unit we want, and what target and lu we wish to *
* talk to. Also tell it where to find the command *
* Also tell it where to read/write the data, and how *
* long the data is supposed to be *
\*******************************************************/
int cd_scsi_cmd(unit
,scsi_cmd
,cmdlen
,data_addr
,datalen
,timeout
,bp
,flags
)
struct scsi_generic
*scsi_cmd
;
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
if(scsi_debug
& PRINTROUTINES
) printf("\ncd_scsi_cmd%d ",unit
);
if(!(cd
->sc_sw
)) /* If we have a scsi driver */
/* !? how'd we GET here? */
panic("attempt to run bad cd device");
xs
= cd_get_xs(unit
,flags
); /* should wait unless booting */
printf("cd%d: scsi_cmd controller busy"
" (this should never happen)\n",unit
);
/*******************************************************\
* Fill out the scsi_xfer structure *
\*******************************************************/
xs
->retries
= CD_RETRIES
;
xs
->when_done
= (flags
& SCSI_NOMASK
)
retry
: xs
->error
= XS_NOERROR
;
/*******************************************************\
* Do the transfer. If we are polling we will return: *
* COMPLETE, Was poll, and cd_done has been called *
* HAD_ERROR, Was poll and an error was encountered *
* TRY_AGAIN_LATER, Adapter short resources, try again *
* if under full steam (interrupts) it will return: *
* SUCCESSFULLY_QUEUED, will do a wakeup when complete *
* HAD_ERROR, had an erro before it could queue *
* TRY_AGAIN_LATER, (as for polling) *
* After the wakeup, we must still check if it succeeded *
* If we have a bp however, all the error proccessing *
* and the buffer code both expect us to return straight *
* to them, so as soon as the command is queued, return *
\*******************************************************/
retval
= (*(cd
->sc_sw
->scsi_cmd
))(xs
);
if(bp
) return retval
; /* will sleep (or not) elsewhere */
/*******************************************************\
* Only here for non I/O cmds. It's cheaper to process *
* the error status here than at interrupt time so *
* sd_done will have done nothing except wake us up. *
\*******************************************************/
case SUCCESSFULLY_QUEUED
:
while(!(xs
->flags
& ITSDONE
))
/* Fall through to check the result */
case XS_NOERROR
: /* usually this one */
retval
= (cd_interpret_sense(unit
,xs
));
/* should sleep here 1 sec */
printf("cd%d: unknown error category from scsi driver\n"
if(tsleep( 0,PRIBIO
+ 2,"retry",hz
* 2))
/*******************************************************\
* we have finished doing the command, free the struct *
* and check if anyone else needs it *
\*******************************************************/
cd_free_xs(unit
,xs
,flags
);
cdstart(unit
); /* check if anything is waiting for the xs */
/***************************************************************\
* Look at the returned sense and act on the error and detirmine *
* The unix error number to pass back... (0 = report no error) *
\***************************************************************/
int cd_interpret_sense(unit
,xs
)
struct scsi_sense_data
*sense
;
struct cd_data
*cd
= cd_driver
->cd_data
[unit
];
static char *error_mes
[] = { "soft error (corrected)",
"not ready", "medium error",
"non-media hardware failure", "illegal request",
"unit attention", "readonly device",
"no data found", "vendor unique",
"copy aborted", "command aborted",
"search returned equal", "volume overflow",
"verify miscompare", "unknown error key"
/***************************************************************\
* If the flags say errs are ok, then always return ok. *
\***************************************************************/
if (xs
->flags
& SCSI_ERR_OK
) return(ESUCCESS
);
silent
= (xs
->flags
& SCSI_SILENT
);
printf("code%x valid%x\n"
,sense
->error_code
& SSD_ERRCODE
,sense
->error_code
& SSD_ERRCODE_VALID
? 1 : 0);
printf("seg%x key%x ili%x eom%x fmark%x\n"
,sense
->ext
.extended
.segment
,sense
->ext
.extended
.flags
& SSD_KEY
,sense
->ext
.extended
.flags
& SSD_ILI
? 1 : 0
,sense
->ext
.extended
.flags
& SSD_EOM
? 1 : 0
,sense
->ext
.extended
.flags
& SSD_FILEMARK
? 1 : 0);
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
);
while(count
< sense
->ext
.extended
.extra_len
)
printf ("%x ",sense
->ext
.extended
.extra_bytes
[count
++]);
switch(sense
->error_code
& SSD_ERRCODE
)
/***************************************************************\
* If it's code 70, use the extended stuff and interpret the key *
\***************************************************************/
case 0x71:/* delayed error */
printf("cd%d: DELAYED ERROR, key = 0x%x\n",unit
,key
);
if(sense
->error_code
& SSD_ERRCODE_VALID
)
info
= ntohl(*((long *)sense
->ext
.extended
.info
));
key
=sense
->ext
.extended
.flags
& SSD_KEY
;
printf("cd%d: %s", unit
, error_mes
[key
- 1]);
if(sense
->error_code
& SSD_ERRCODE_VALID
)
case 0x2: /* NOT READY */
case 0x5: /* ILLEGAL REQUEST */
case 0x6: /* UNIT ATTENTION */
case 0x7: /* DATA PROTECT */
case 0x8: /* BLANK CHECK */
printf(", requested size: %d (decimal)",
printf(", info = %d (decimal)", info
);
case 0x1: /* RECOVERED ERROR */
case 0x2: /* NOT READY */
cd
->flags
&= ~(CDVALID
| CDHAVELABEL
);
case 0x5: /* ILLEGAL REQUEST */
case 0x6: /* UNIT ATTENTION */
cd
->flags
&= ~(CDVALID
| CDHAVELABEL
);
case 0x7: /* DATA PROTECT */
case 0xd: /* VOLUME OVERFLOW */
case 0x8: /* BLANK CHECK */
/*******************************\
* Not code 70, just report it *
\*******************************/
printf("cd%d: error code %d", unit
,
sense
->error_code
& SSD_ERRCODE
);
if(sense
->error_code
& SSD_ERRCODE_VALID
)
printf(" at block no. %d (decimal)",
(sense
->ext
.unextended
.blockhi
<<16)
+ (sense
->ext
.unextended
.blockmed
<<8)
+ (sense
->ext
.unextended
.blocklow
));
printf("------------------------------");
for (y
= 0; y
<num
; y
+= 1)
printf("%02x ",*address
++);
printf("\n------------------------------\n");