3238 lines
77 KiB
C
3238 lines
77 KiB
C
#ident "@(#)sr.c 1.1 94/10/31 Copyr 1990 Sun Micro"
|
||
#include "sr.h"
|
||
#if NSR > 0
|
||
/************************************************************************
|
||
*************************************************************************
|
||
** *
|
||
** *
|
||
** SCSI CDROM driver (for CDROM, read-only) *
|
||
** for SunOs v4.0, 4.0.1, 4.0.3 & 4.1 *
|
||
** *
|
||
** Copyright (c) 1989 by Sun Microsystem, Inc. *
|
||
** *
|
||
*************************************************************************
|
||
*************************************************************************
|
||
*/
|
||
|
||
|
||
#define SRDEBUG /* Allow compiling of debug code */
|
||
#define REL4 /* enable release 4.00 mods */
|
||
|
||
|
||
/*
|
||
** SCSI driver for SCSI disks.
|
||
*/
|
||
#ifndef REL4
|
||
#include "sr.h"
|
||
#include "../h/param.h"
|
||
#include "../h/systm.h"
|
||
#include "../h/dk.h"
|
||
#include "../h/buf.h"
|
||
#include "../h/conf.h"
|
||
#include "../h/dir.h"
|
||
#include "../h/user.h"
|
||
#include "../h/map.h"
|
||
#include "../h/vmmac.h"
|
||
#include "../h/ioctl.h"
|
||
#include "../h/uio.h"
|
||
#include "../h/kernel.h"
|
||
#include "../h/dkbad.h"
|
||
#include "../h/fcntl.h"
|
||
#include "../h/file.h"
|
||
#include "../h/proc.h"
|
||
|
||
#include "../machine/pte.h"
|
||
#include "../machine/psl.h"
|
||
#include "../machine/mmu.h"
|
||
#include "../machine/cpu.h"
|
||
|
||
#include "../sun/dklabel.h"
|
||
#include "../sun/dkio.h"
|
||
|
||
#include "../sundev/mbvar.h"
|
||
#include "../sundev/screg.h"
|
||
#include "../sundev/sireg.h"
|
||
#include "../sundev/scsi.h"
|
||
#include "../sundev/srreg.h"
|
||
|
||
#else REL4
|
||
#include <sys/param.h>
|
||
#include <sys/systm.h>
|
||
#include <sys/dk.h>
|
||
#include <sys/buf.h>
|
||
#include <sys/conf.h>
|
||
#include <sys/user.h>
|
||
#include <sys/map.h>
|
||
#include <sys/vmmac.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/uio.h>
|
||
#include <sys/kernel.h>
|
||
#include <sys/dkbad.h>
|
||
#include <sys/fcntl.h>
|
||
#include <sys/file.h>
|
||
#include <sys/kmem_alloc.h>
|
||
|
||
#include <machine/pte.h>
|
||
#include <machine/psl.h>
|
||
#include <machine/mmu.h>
|
||
#include <machine/cpu.h>
|
||
|
||
#include <sun/dklabel.h>
|
||
#include <sun/dkio.h>
|
||
|
||
#include <sundev/mbvar.h>
|
||
#include <sundev/screg.h>
|
||
#include <sundev/sireg.h>
|
||
#include <sundev/scsi.h>
|
||
#include <sundev/srreg.h>
|
||
#endif REL4
|
||
|
||
|
||
#define MAX_CDROM 8
|
||
#define SR_TIMEOUT /* define for scsi recovery */
|
||
#define MAX_RETRIES 4 /* retry limit */
|
||
#define MAX_RESTORES 4 /* rezero unit limit (after retries) */
|
||
#define MAX_FAILS 20 /* soft errors before reassign */
|
||
#define MAX_LABEL_RETRIES 2
|
||
#define MAX_LABEL_RESTORES 1
|
||
#define MAX_BUSY 10
|
||
#define EL_RETRY 2 /* Error msg threshold, retries */
|
||
#define EL_REST 0 /* Error msg threshold, restores */
|
||
#define EL_FAILS 10 /* Error msg threshold, soft errors */
|
||
#define CD_SIZE 0x400000 /* default dev size = 2**21==2E6 */
|
||
/* typical size = 560 Mb =~ 1E6 blks */
|
||
|
||
|
||
#define LPART(dev) (dev & (NLPART - 1))
|
||
#define SRUNIT(dev) ((dev >> 3) & (NUNIT - 1))
|
||
#define SRNUM(un) (un - srunits)
|
||
#define SRIOPBALLOC(size) ((caddr_t)rmalloc(iopbmap, (long)(size + 4)))
|
||
#define SRIOPBFREE(ptr, size) (rmfree(iopbmap, (long)(size +4), (u_long)ptr))
|
||
|
||
|
||
#ifdef SRDEBUG
|
||
short sr_debug = 0; /*
|
||
** 0 = normal operation
|
||
** 1 = extended error info only
|
||
** 2 = debug and error info
|
||
** 3 = all status info
|
||
*/
|
||
/* Handy debugging 0, 1, and 2 argument printfs */
|
||
#define SR_PRINT_CMD(un) \
|
||
if (sr_debug > 1) sr_print_cmd(un)
|
||
#define DPRINTF(str) \
|
||
if (sr_debug > 1) printf(str)
|
||
#define DPRINTF1(str, arg1) \
|
||
if (sr_debug > 1) printf(str, arg1)
|
||
#define DPRINTF2(str, arg1, arg2) \
|
||
if (sr_debug > 1) printf(str, arg1, arg2)
|
||
|
||
/* Handy extended error reporting 0, 1, and 2 argument printfs */
|
||
#define EPRINTF(str) \
|
||
if (sr_debug) printf(str)
|
||
#define EPRINTF1(str, arg1) \
|
||
if (sr_debug) printf(str, arg1)
|
||
#define EPRINTF2(str, arg1, arg2) \
|
||
if (sr_debug) printf(str, arg1, arg2)
|
||
#define DEBUG_DELAY(cnt) \
|
||
if (sr_debug) DELAY(cnt)
|
||
|
||
#else SRDEBUG
|
||
#define SR_PRINT_CMD(un)
|
||
#define DPRINTF(str)
|
||
#define DPRINTF1(str, arg2)
|
||
#define DPRINTF2(str, arg1, arg2)
|
||
#define EPRINTF(str)
|
||
#define EPRINTF1(str, arg2)
|
||
#define EPRINTF2(str, arg1, arg2)
|
||
#define DEBUG_DELAY(cnt)
|
||
#endif SRDEBUG
|
||
|
||
extern char *strncpy();
|
||
|
||
extern struct scsi_unit srunits[];
|
||
extern struct scsi_unit_subr scsi_unit_subr[];
|
||
extern struct scsi_disk srdisk[];
|
||
extern int ncdrom;
|
||
|
||
short sr_max_retries = MAX_RETRIES;
|
||
short sr_max_restores = MAX_RESTORES;
|
||
short sr_max_fails = MAX_FAILS;
|
||
short sr_restores = EL_REST;
|
||
short sr_fails = EL_FAILS;
|
||
short disconenbl = 1; /* allow disconnect / re-connect on cdrom */
|
||
/* set to 0 to disable disconnect - reconnect */
|
||
|
||
/*
|
||
** flag locking and unlocking purposes.
|
||
** to make sure that only the very last close
|
||
** unlocks the drive
|
||
*/
|
||
struct cdrom_open_flag {
|
||
u_char cdopen_flag0;
|
||
u_char cdopen_flag1;
|
||
u_char cdxopen_flag; /* exclusive open flag */
|
||
} sr_open_flag[MAX_CDROM];
|
||
|
||
static void sr_setup_openflag();
|
||
|
||
/*
|
||
** A string to keep track of the vendor id.
|
||
** This is needed since the scsi_disk structure does not
|
||
** has a place to put this information.
|
||
*/
|
||
struct cdrom_inquiry {
|
||
char cdrom_vid[8]; /* vendor ID */
|
||
char cdrom_pid[16]; /* product ID */
|
||
char cdrom_rev[4]; /* revision level */
|
||
} sr_inq[MAX_CDROM];
|
||
|
||
#ifndef SRDEBUG
|
||
short sr_retry = EL_RETRY; /*
|
||
* Error message threshold, retries.
|
||
* Make it global so manufacturing can
|
||
* override setting.
|
||
*/
|
||
|
||
#else SRDEBUG
|
||
short sr_retry = 0; /* For debug, set retries to 0. */
|
||
|
||
/************************************************************************
|
||
** *
|
||
** Local Function Declarations *
|
||
** *
|
||
*************************************************************************/
|
||
static int sr_medium_removal();
|
||
static int sr_pause_resume(), sr_play_msf(), sr_play_trkind();
|
||
static int sr_read_tochdr(), sr_read_tocentry();
|
||
|
||
sr_print_buffer(y, count)
|
||
register u_char *y;
|
||
register int count;
|
||
{
|
||
register int x;
|
||
|
||
for (x = 0; x < count; x++)
|
||
printf("%x ", *y++);
|
||
printf("\n");
|
||
}
|
||
#endif SRDEBUG
|
||
|
||
|
||
/*
|
||
** Return a pointer to this unit's unit structure.
|
||
*/
|
||
srunitptr(md)
|
||
register struct mb_device *md;
|
||
{
|
||
return ((int)&srunits[md->md_unit]);
|
||
}
|
||
|
||
|
||
static
|
||
srtimer(dev)
|
||
register dev_t dev;
|
||
{
|
||
register struct scsi_disk *dsi;
|
||
register struct scsi_unit *un;
|
||
register struct scsi_ctlr *c;
|
||
register u_int unit;
|
||
|
||
unit = SRUNIT(dev);
|
||
un = &srunits[unit];
|
||
dsi = &srdisk[unit];
|
||
c = un->un_c;
|
||
|
||
/* DPRINTF("srtimer:\n"); */
|
||
if (dsi->un_openf >= OPENING) {
|
||
(*un->un_c->c_ss->scs_start)(un);
|
||
|
||
#ifdef SR_TIMEOUT
|
||
if ((dsi->un_timeout != 0) && (--dsi->un_timeout == 0)) {
|
||
/* Process command timeout for normal I/O operations */
|
||
printf("sr%d: srtimer: I/O request timeout\n", unit);
|
||
if ((*c->c_ss->scs_deque)(c, un)) {
|
||
/* Can't Find CDB I/O request to kill, help! */
|
||
printf("sr%d: srtimer: can't abort request\n",
|
||
unit);
|
||
(*un->un_c->c_ss->scs_reset)(un->un_c, 0);
|
||
}
|
||
} else if (dsi->un_timeout != 0) {
|
||
DPRINTF2("srtimer: running, open= %d, timeout= %d\n",
|
||
dsi->un_openf, dsi->un_timeout);
|
||
}
|
||
#endif SR_TIMEOUT
|
||
|
||
/* Process opening delay timeout */
|
||
} else if ((dsi->un_timeout != 0) && (--dsi->un_timeout == 0)) {
|
||
DPRINTF("srtimer: running...\n");
|
||
wakeup((caddr_t)dev);
|
||
}
|
||
timeout(srtimer, (caddr_t)dev, 30*hz);
|
||
}
|
||
|
||
/* */
|
||
/************************************************************************
|
||
*************************************************************************
|
||
** *
|
||
** *
|
||
** Autoconfiguration Routines *
|
||
** *
|
||
** *
|
||
*************************************************************************
|
||
/************************************************************************/
|
||
|
||
/*
|
||
** Attach device (boot time). The controller is there. Since this is a
|
||
** CDROM, there is no need to check the label.
|
||
*/
|
||
|
||
srattach(md)
|
||
register struct mb_device *md;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
struct scsi_inquiry_data *sid;
|
||
int i;
|
||
|
||
dsi = &srdisk[md->md_unit];
|
||
un = &srunits[md->md_unit];
|
||
sr_open_flag[md->md_unit].cdopen_flag0 = 0;
|
||
sr_open_flag[md->md_unit].cdopen_flag1 = 0;
|
||
sr_open_flag[md->md_unit].cdxopen_flag = 0;
|
||
|
||
/*
|
||
* link in, fill in unit struct.
|
||
*/
|
||
un->un_md = md;
|
||
un->un_mc = md->md_mc;
|
||
un->un_unit = md->md_unit;
|
||
un->un_target = TARGET(md->md_slave);
|
||
un->un_lun = LUN(md->md_slave);
|
||
un->un_ss = &scsi_unit_subr[TYPE(md->md_flags)];
|
||
un->un_present = 0;
|
||
|
||
dsi->un_openf = OPENING;
|
||
dsi->un_flags = 0;
|
||
dsi->un_timeout = 0;
|
||
dsi->un_timer = 0;
|
||
dsi->un_bad_index = 0;
|
||
dsi->un_status = 0;
|
||
dsi->un_retries = 0;
|
||
dsi->un_restores = 0;
|
||
dsi->un_sec_left = 0;
|
||
dsi->un_total_errors = 0;
|
||
dsi->un_err_resid = 0;
|
||
dsi->un_err_blkno = 0;
|
||
dsi->un_cyl_start = 0;
|
||
dsi->un_cyl_end = 0;
|
||
dsi->un_cylin_last = 0;
|
||
dsi->un_lp1 = NULL;
|
||
dsi->un_lp2 = NULL;
|
||
dsi->un_ctype = CTYPE_UNKNOWN;
|
||
|
||
/*
|
||
* Set default disk geometry and partition table.
|
||
* This is necessary so we can later open the device
|
||
* if we don't find it at probe time.
|
||
*/
|
||
bzero((caddr_t)&(dsi->un_g), sizeof (struct dk_geom));
|
||
bzero((caddr_t)dsi->un_map, sizeof (struct dk_allmap));
|
||
dsi->un_g.dkg_ncyl = 1;
|
||
dsi->un_g.dkg_acyl = 0;
|
||
dsi->un_g.dkg_pcyl = 1;
|
||
dsi->un_g.dkg_nhead = 1;
|
||
dsi->un_g.dkg_nsect = 1;
|
||
dsi->un_cyl_size = 1;
|
||
|
||
/*
|
||
* Allocate space for request sense/inquiry buffer in
|
||
* memory. Align it to a longword boundary.
|
||
*/
|
||
sid = (struct scsi_inquiry_data *)rmalloc(iopbmap,
|
||
(long)(sizeof (struct scsi_inquiry_data) +4));
|
||
if (sid == NULL) {
|
||
printf("sr%d: srattach: no space for inquiry data\n",
|
||
SRNUM(un));
|
||
return;
|
||
}
|
||
|
||
while ((u_int)sid & 0x03)
|
||
((u_int)sid)++;
|
||
dsi->un_sense = (int)sid;
|
||
|
||
EPRINTF2("sr%d: srattach: buffer= 0x%x, ", SRNUM(un), (int)sid);
|
||
EPRINTF2("dsi= 0x%x, *scsi_unit= 0x%x\n", dsi, un);
|
||
DPRINTF2("srattach: looking for lun %d on target %d\n",
|
||
LUN(md->md_slave), TARGET(md->md_slave));
|
||
|
||
/*
|
||
* Test for unit ready. The first test checks
|
||
* for a non-existant device. The other tests
|
||
* wait for the drive to get ready.
|
||
*/
|
||
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) > 1) {
|
||
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) > 1) {
|
||
DPRINTF("srattach: unit offline\n");
|
||
return;
|
||
}
|
||
}
|
||
for (i = 0; i < MAX_BUSY; i++) {
|
||
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) == 0) {
|
||
goto SRATTACH_UNIT;
|
||
|
||
} else if (un->un_scb.chk) {
|
||
goto SRATTACH_UNIT;
|
||
|
||
} else if (un->un_scb.busy && !un->un_scb.is) {
|
||
EPRINTF("srattach: unit busy\n");
|
||
DELAY(6000000); /* Wait 6 Sec. */
|
||
|
||
} else if (un->un_scb.is) {
|
||
EPRINTF("srattach: reservation conflict\n");
|
||
break;
|
||
}
|
||
}
|
||
printf("srattach: unit offline\n");
|
||
return;
|
||
|
||
SRATTACH_UNIT:
|
||
if (simple(un, SC_TEST_UNIT_READY, 0, 0, 0) != 0) {
|
||
DPRINTF("srattach: unit failed\n");
|
||
return;
|
||
}
|
||
|
||
bzero((caddr_t)sid, sizeof (struct scsi_inquiry_data));
|
||
bzero((caddr_t)&sr_inq[md->md_unit], sizeof (struct cdrom_inquiry));
|
||
if (simple(un, SC_INQUIRY, (char *) sid - DVMA, 0,
|
||
(int)sizeof (struct scsi_inquiry_data)) == 0) {
|
||
/* Only CCS SCSI-1 compliant controllers support Inquiry cmd */
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 2)
|
||
sr_print_buffer((u_char *)sid, 32);
|
||
#endif SRDEBUG
|
||
if ((bcmp(sid->vid, "TOSHIBA CD-ROM",
|
||
sizeof (sid->vid)) == 0) ||
|
||
(bcmp(sid->vid, "SONY", 4) == 0)) {
|
||
/* printf("sr0: %s found\n", sid->vid); */
|
||
dsi->un_ctype = CTYPE_TOSHIBA;
|
||
dsi->un_flags = SC_UNF_NO_DISCON;
|
||
if (i = simple(un, SR_MEDIA_REMOV, 0, 0,
|
||
SR_MEDIA_unlock)) {
|
||
EPRINTF1("srattach: media-unlock failed:%x\n", i);
|
||
return;
|
||
}
|
||
dsi->un_flags = 0;
|
||
} else {
|
||
/* printf("sr0: %s found\n", sid->vid); */
|
||
printf(" defaulting to generic cdrom\n");
|
||
dsi->un_ctype = CTYPE_CDROM;
|
||
/* play it safe if unknown cdrom */
|
||
dsi->un_flags = SC_UNF_NO_DISCON;
|
||
}
|
||
bcopy(sid->vid, (caddr_t)sr_inq[md->md_unit].cdrom_vid, 8);
|
||
bcopy(sid->pid, (caddr_t)sr_inq[md->md_unit].cdrom_pid, 16);
|
||
bcopy(sid->revision,
|
||
(caddr_t)sr_inq[md->md_unit].cdrom_rev, 4);
|
||
} else {
|
||
/* non-CCS, assume Adaptec ACB-4000 */
|
||
EPRINTF("srattach: non-CCS Adaptec found\n");
|
||
dsi->un_ctype = CTYPE_ACB4000;
|
||
dsi->un_flags = SC_UNF_NO_DISCON;
|
||
}
|
||
un->un_present = 1; /* "it's here...(#2)" */
|
||
dsi->un_openf = OPEN;
|
||
}
|
||
|
||
|
||
|
||
/*
|
||
** Run a command in polled mode.
|
||
** Return true if successful, false otherwise.
|
||
*/
|
||
|
||
static
|
||
simple(un, cmd, dma_addr, secno, nsect)
|
||
register struct scsi_unit *un;
|
||
int cmd, dma_addr, secno, nsect;
|
||
{
|
||
register struct scsi_cdb *cdb;
|
||
register struct scsi_ctlr *c;
|
||
register int err;
|
||
|
||
/*
|
||
* Grab and clear the command block.
|
||
*/
|
||
c = un->un_c;
|
||
cdb = &un->un_cdb;
|
||
bzero((caddr_t)cdb, CDB_GROUP1);
|
||
|
||
/*
|
||
* Plug in command block values.
|
||
*/
|
||
cdb->cmd = cmd;
|
||
if (SCSI_RECV_DATA_CMD(cmd))
|
||
un->un_flags |= SC_UNF_RECV_DATA;
|
||
else
|
||
un->un_flags &= ~SC_UNF_RECV_DATA;
|
||
|
||
c->c_un = un;
|
||
cdb->lun = un->un_lun;
|
||
FORMG0ADDR(cdb, secno);
|
||
FORMG0COUNT(cdb, nsect);
|
||
un->un_dma_addr = dma_addr;
|
||
if (cmd == SC_INQUIRY)
|
||
un->un_dma_count = nsect;
|
||
else
|
||
un->un_dma_count = nsect << SECDIV;
|
||
|
||
/*
|
||
* Fire up the pup.
|
||
*/
|
||
if (err = (*c->c_ss->scs_cmd)(c, un, 0)) {
|
||
if (err > 1) {
|
||
return (2); /* Hard failure */
|
||
} else {
|
||
return (1); /* Recoverable failure */
|
||
}
|
||
}
|
||
return (0); /* No failure */
|
||
}
|
||
|
||
/*^L*/
|
||
/************************************************************************
|
||
*************************************************************************
|
||
** *
|
||
** *
|
||
** Unix Entry Points *
|
||
** *
|
||
** *
|
||
*************************************************************************
|
||
/************************************************************************/
|
||
|
||
/*
|
||
** This routine opens a disk. Note that we can handle disks
|
||
** that make an appearance after boot time.
|
||
*/
|
||
/*ARGSUSED*/
|
||
sropen(dev, flag)
|
||
dev_t dev;
|
||
int flag;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register struct dk_map *lp;
|
||
register int unit;
|
||
register struct scsi_disk *dsi;
|
||
register struct scsi_inquiry_data *sid;
|
||
|
||
unit = SRUNIT(dev);
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("sropen: illegal unit\n");
|
||
return (ENXIO);
|
||
}
|
||
|
||
un = &srunits[unit];
|
||
dsi = &srdisk[unit];
|
||
if (un->un_mc == 0) {
|
||
EPRINTF("sropen: disk not attached: no controller\n");
|
||
return (ENXIO); }
|
||
|
||
DPRINTF2("sropen: unit %x flag %x\n", unit, flag);
|
||
/*
|
||
* If command timeouts not activated yet, switch it on.
|
||
*/
|
||
#ifdef SR_TIMEOUT
|
||
if (dsi->un_timer == 0) {
|
||
EPRINTF("sropen: starting timer\n");
|
||
dsi->un_timer++;
|
||
timeout(srtimer, (caddr_t)dev, 30*hz);
|
||
}
|
||
#endif SR_TIMEOUT
|
||
|
||
/*
|
||
* Check for special opening mode.
|
||
*/
|
||
lp = &dsi->un_map[LPART(dev)];
|
||
if (un->un_present) {
|
||
/*
|
||
* check for previous exclusive open
|
||
*/
|
||
if ((sr_open_flag[unit].cdxopen_flag) ||
|
||
(flag & FEXCL) && ((sr_open_flag[unit].cdopen_flag0) ||
|
||
(sr_open_flag[unit].cdopen_flag1))) {
|
||
return (EBUSY);
|
||
}
|
||
dsi->un_openf = OPENING;
|
||
if ((srcmd(dev, SR_START_STOP, 0, SR_START_load,
|
||
(caddr_t)0, SD_SILENT, (caddr_t)0)) &&
|
||
(srcmd(dev, SR_START_STOP, 0, SR_START_load,
|
||
(caddr_t)0, SD_SILENT, (caddr_t)0))) {
|
||
return (EIO);
|
||
}
|
||
(void) srcmd(dev, SR_MEDIA_REMOV, 0,
|
||
SR_MEDIA_lock, (caddr_t)0, 0, (caddr_t)0);
|
||
dsi->un_openf = OPEN;
|
||
sr_setup_openflag(&(sr_open_flag[unit]), (u_char)major(dev));
|
||
|
||
/*
|
||
* set up exclusive open flags
|
||
*/
|
||
if (flag & FEXCL) {
|
||
sr_open_flag[unit].cdxopen_flag = 1;
|
||
} else {
|
||
sr_open_flag[unit].cdxopen_flag = 0;
|
||
}
|
||
|
||
return (0);
|
||
|
||
/*
|
||
* Didn't see it at autoconfig time? Let's look again..
|
||
*/
|
||
} else {
|
||
DPRINTF1("sropen: opening device: lp->dkl_nblk:%x\n",
|
||
lp->dkl_nblk);
|
||
lp->dkl_nblk = CD_SIZE;
|
||
|
||
dsi->un_openf = OPENING;
|
||
dsi->un_flags = SC_UNF_NO_DISCON; /* no disconnects */
|
||
if (srcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0,
|
||
0, (caddr_t)0) && dsi->un_openf == CLOSED) {
|
||
EPRINTF("sropen: not ready\n");
|
||
dsi->un_timer = 0;
|
||
untimeout(srtimer, (caddr_t)dev);
|
||
return (ENXIO);
|
||
}
|
||
dsi->un_openf = OPENING;
|
||
dsi->un_flags = SC_UNF_NO_DISCON; /* no disconnects */
|
||
if ((srcmd(dev, SR_START_STOP, 0, SR_START_load,
|
||
(caddr_t)0, SD_SILENT, (caddr_t)0)) &&
|
||
(srcmd(dev, SR_START_STOP, 0, SR_START_load,
|
||
(caddr_t)0, SD_SILENT, (caddr_t)0))) {
|
||
return (EIO);
|
||
}
|
||
(void)srcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0,
|
||
(caddr_t)0);
|
||
dsi->un_openf = OPENING;
|
||
if (srcmd(dev, SC_TEST_UNIT_READY, 0, 0, (caddr_t)0, 0,
|
||
(caddr_t)0)) {
|
||
EPRINTF("sropen: not ready\n");
|
||
dsi->un_timer = 0;
|
||
untimeout(srtimer, (caddr_t)dev);
|
||
return (ENXIO);
|
||
}
|
||
dsi->un_openf = OPENING;
|
||
(int)sid = dsi->un_sense; /* Inquiry data buffer */
|
||
bzero((caddr_t)sid, sizeof (struct scsi_inquiry_data));
|
||
bzero((caddr_t)&sr_inq[unit], sizeof (struct cdrom_inquiry));
|
||
if (srcmd(dev, SC_INQUIRY, 0,
|
||
sizeof (struct scsi_inquiry_data),
|
||
(caddr_t)sid, 0, (caddr_t)0)) {
|
||
/* non-CCS, assume Adaptec */
|
||
EPRINTF("sropen: Adaptec found\n");
|
||
dsi->un_ctype = CTYPE_ACB4000;
|
||
dsi->un_flags = SC_UNF_NO_DISCON;
|
||
} else {
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 2) sr_print_buffer((u_char *)sid, 32);
|
||
#endif SRDEBUG
|
||
if (sid->dtype != 5) {
|
||
printf("sropen - not cdrom: %d \n",
|
||
sid->dtype);
|
||
}
|
||
dsi->un_flags = 0;
|
||
/* printf("sr0: %s found\n", sid->vid); */
|
||
if ((bcmp(sid->vid, "TOSHIBA CD-ROM",
|
||
sizeof (sid->vid)) == 0) ||
|
||
(bcmp(sid->vid, "SONY ", 4) == 0)) {
|
||
EPRINTF("sropen: CCS compliant found\n");
|
||
dsi->un_ctype = CTYPE_TOSHIBA;
|
||
un->un_present = 1; /* "it's here...#3" */
|
||
dsi->un_openf = OPENING;
|
||
dsi->un_openf = OPENING;
|
||
(void) srcmd(dev, SR_START_STOP, 0,
|
||
SR_START_load, (caddr_t)0, 0, (caddr_t)0);
|
||
dsi->un_openf = OPENING;
|
||
(void) srcmd(dev, SR_MEDIA_REMOV, 0,
|
||
SR_MEDIA_lock, (caddr_t)0, 0, (caddr_t)0);
|
||
dsi->un_openf = OPEN;
|
||
sr_setup_openflag(&(sr_open_flag[unit]),
|
||
(u_char)major(dev));
|
||
} else if (sid->rdf != 0x01) {
|
||
EPRINTF("sropen: non-CCS found\n");
|
||
dsi->un_ctype = CTYPE_ACB4000;
|
||
} else {
|
||
EPRINTF("sropen: CCS found\n");
|
||
/*
|
||
** new compliant SCSI 1 default
|
||
** dsi->un_ctype = CTYPE_CCS;
|
||
*/
|
||
printf("assuming CCS compliant CDROM: %s\n",
|
||
sid->vid);
|
||
un->un_present = 1; /* "it's here...#4" */
|
||
dsi->un_ctype = CTYPE_CDROM;
|
||
dsi->un_openf = OPENING;
|
||
(void) srcmd(dev, SR_MEDIA_REMOV, 0,
|
||
SR_MEDIA_lock, (caddr_t)0, 0, (caddr_t)0);
|
||
sr_setup_openflag(&(sr_open_flag[unit]),
|
||
(u_char)major(dev));
|
||
dsi->un_openf = OPEN;
|
||
if (!disconenbl) {
|
||
printf(
|
||
"sr-disconnect-reconnect disabled %x\n",
|
||
disconenbl);
|
||
dsi->un_flags = SC_UNF_NO_DISCON;
|
||
}
|
||
}
|
||
}
|
||
bcopy(sid->vid, (caddr_t)sr_inq[unit].cdrom_vid, 8);
|
||
bcopy(sid->pid, (caddr_t)sr_inq[unit].cdrom_pid, 16);
|
||
bcopy(sid->revision, (caddr_t)sr_inq[unit].cdrom_rev,
|
||
4);
|
||
|
||
/*
|
||
** set up exclusive open flags
|
||
*/
|
||
if (flag & FEXCL) {
|
||
sr_open_flag[unit].cdxopen_flag = 1;
|
||
}
|
||
return (0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
** This routine is added to unlock the drive at hsfs unmount time
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
srclose(dev, mode)
|
||
dev_t dev;
|
||
int mode;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register int unit;
|
||
register struct scsi_disk *dsi;
|
||
|
||
DPRINTF2("sr: last close on dev %d.%d\n", major(dev), minor(dev));
|
||
|
||
unit = SRUNIT(dev);
|
||
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("srclose: illegal unit\n");
|
||
return (ENXIO);
|
||
}
|
||
|
||
un = &srunits[unit];
|
||
dsi = &srdisk[unit];
|
||
if (un->un_mc == 0) {
|
||
EPRINTF("srclose: disk not attached: no controller\n");
|
||
return (ENXIO); }
|
||
|
||
|
||
if (sr_open_flag[unit].cdopen_flag0 == major(dev)) {
|
||
sr_open_flag[unit].cdopen_flag0 = 0;
|
||
} else if (sr_open_flag[unit].cdopen_flag1 == major(dev)) {
|
||
sr_open_flag[unit].cdopen_flag1 = 0;
|
||
} else {
|
||
printf("srclose: sr_open_flag corrupted\n");
|
||
}
|
||
|
||
if (sr_open_flag[unit].cdxopen_flag) {
|
||
sr_open_flag[unit].cdxopen_flag = 0;
|
||
}
|
||
|
||
dsi->un_openf = OPENING;
|
||
|
||
if ((sr_open_flag[unit].cdopen_flag0 == 0) &&
|
||
(sr_open_flag[unit].cdopen_flag1 == 0)) {
|
||
if (srcmd(dev, SR_MEDIA_REMOV, 0, SR_MEDIA_unlock,
|
||
(caddr_t)0, 0, (caddr_t)0)) {
|
||
#if defined(MUNIX) || defined(MINIROOT)
|
||
/*
|
||
** If this is MUNIX or the miniroot then we may be using
|
||
** a magnetic disk to simulate a cdrom for installation.
|
||
** We are lenient on the error from the SR_MEDIA_REMOV
|
||
** command if we suspect a magnetic disk is being used
|
||
** (see setting of un_ctype in srattach() and sropen()).
|
||
*/
|
||
if (dsi->un_ctype != CTYPE_CDROM)
|
||
#endif
|
||
return (ENXIO);
|
||
}
|
||
}
|
||
dsi->un_openf = CLOSED;
|
||
return (0);
|
||
}
|
||
|
||
/*
|
||
** This routine returns the size of a logical partition. It is called
|
||
** from the device switch at normal priority.
|
||
*/
|
||
srsize(dev)
|
||
register dev_t dev;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register struct dk_map *lp;
|
||
register struct scsi_disk *dsi;
|
||
register int unit;
|
||
|
||
unit = SRUNIT(dev);
|
||
if (unit >= ncdrom) {
|
||
return (-1);
|
||
}
|
||
un = &srunits[unit];
|
||
DPRINTF("srsize:\n");
|
||
|
||
if (un->un_present) {
|
||
dsi = &srdisk[SRUNIT(dev)];
|
||
lp = &dsi->un_map[LPART(dev)];
|
||
return (lp->dkl_nblk = CD_SIZE); /* = 2**22 == 4 mb */
|
||
} else {
|
||
EPRINTF("srsize: unit not present\n");
|
||
return (CD_SIZE); /* = 2**22 == 4 mb */
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** This routine is the focal point of internal commands to the controller.
|
||
** NOTE: this routine assumes that all operations done before the disk's
|
||
** geometry is defined. IT IS CALLED FROM THE BOTTOM HALF.
|
||
*/
|
||
|
||
srcmd(dev, cmd, block, count, addr, options, uscsi_cmd)
|
||
dev_t dev;
|
||
int cmd, block, count;
|
||
caddr_t addr;
|
||
int options;
|
||
caddr_t uscsi_cmd;
|
||
{
|
||
register struct scsi_disk *dsi;
|
||
register struct scsi_unit *un;
|
||
register struct buf *bp;
|
||
int s;
|
||
long b_flags;
|
||
int flag;
|
||
struct uscsi_cmd *scmd;
|
||
|
||
un = &srunits[SRUNIT(dev)];
|
||
dsi = &srdisk[SRUNIT(dev)];
|
||
bp = &un->un_sbuf;
|
||
|
||
if (uscsi_cmd != (caddr_t)0) {
|
||
scmd = (struct uscsi_cmd *)uscsi_cmd;
|
||
flag = (scmd->uscsi_flags & USCSI_READ) ? B_READ : B_WRITE;
|
||
} else {
|
||
flag = B_WRITE;
|
||
}
|
||
|
||
/* DPRINTF("srcmd:\n"); */
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
while (bp->b_flags & B_BUSY) {
|
||
bp->b_flags |= B_WANTED;
|
||
(void) sleep((caddr_t) bp, PRIBIO);
|
||
}
|
||
|
||
bp->b_flags = B_BUSY | flag;
|
||
(void) splx(s);
|
||
|
||
un->un_scmd = cmd;
|
||
bp->b_dev = dev;
|
||
bp->b_bcount = count;
|
||
bp->b_blkno = block;
|
||
(caddr_t)bp->b_un.b_addr = addr;
|
||
dsi->un_options = options;
|
||
|
||
un->un_scratch_addr = uscsi_cmd;
|
||
|
||
/*
|
||
* Execute the I/O request.
|
||
*/
|
||
|
||
srstrategy(bp);
|
||
(void) iowait(bp);
|
||
|
||
/* s = splr(pritospl(un->un_mc->mc_intpri)); */
|
||
bp->b_flags &= ~B_BUSY;
|
||
b_flags = bp->b_flags;
|
||
if (bp->b_flags & B_WANTED) {
|
||
/* DPRINTF1("srcmd: waking..., bp= 0x%x\n", bp); */
|
||
wakeup((caddr_t)bp);
|
||
}
|
||
/* (void) splx(s); */
|
||
return (b_flags & B_ERROR);
|
||
}
|
||
|
||
|
||
/*
|
||
** This routine is the high level interface to the disk. It performs
|
||
** reads and writes on the disk using the buf as the method of communication.
|
||
** It is called from the device switch for block operations and via physio()
|
||
** for raw operations. It is called at normal priority.
|
||
*/
|
||
srstrategy(bp)
|
||
register struct buf *bp;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register struct mb_device *md;
|
||
register struct dk_map *lp;
|
||
register u_int bn;
|
||
register struct diskhd *dh;
|
||
register struct scsi_disk *dsi;
|
||
register int s;
|
||
int unit;
|
||
|
||
DPRINTF("srstrategy:\n");
|
||
unit = dkunit(bp);
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("srstrategy: invalid unit\n");
|
||
bp->b_flags |= B_ERROR;
|
||
iodone(bp);
|
||
return;
|
||
}
|
||
un = &srunits[unit];
|
||
md = un->un_md;
|
||
dsi = &srdisk[unit];
|
||
lp = &dsi->un_map[LPART(bp->b_dev)];
|
||
bn = bp->b_blkno;
|
||
|
||
/* Check for EOM */
|
||
if ((bp != &un->un_sbuf) && (un->un_present && (bn >= lp->dkl_nblk))) {
|
||
printf("srstrategy:%x invalid block addr: %x >= %x\n",
|
||
bp, bn, lp->dkl_nblk);
|
||
bp->b_resid = bp->b_bcount;
|
||
iodone(bp);
|
||
return;
|
||
}
|
||
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
dh = &md->md_utab;
|
||
disksort(dh, bp);
|
||
|
||
/*
|
||
* call unit start routine to queue up device, if it
|
||
* currently isn't queued.
|
||
*/
|
||
(*un->un_c->c_ss->scs_ustart)(un);
|
||
|
||
/* call start routine to run the next SCSI command */
|
||
(*un->un_c->c_ss->scs_start)(un);
|
||
(void) splx(s);
|
||
}
|
||
|
||
|
||
/*
|
||
** Set up a transfer for the controller
|
||
*/
|
||
srstart(bp, un)
|
||
register struct buf *bp;
|
||
register struct scsi_unit *un;
|
||
{
|
||
register struct dk_map *lp;
|
||
register struct scsi_disk *dsi;
|
||
register int nblk;
|
||
|
||
DPRINTF("srstart:\n");
|
||
|
||
dsi = &srdisk[dkunit(bp)];
|
||
lp = &dsi->un_map[LPART(bp->b_dev)];
|
||
|
||
/* Process internal I/O requests */
|
||
if (bp == &un->un_sbuf) {
|
||
DPRINTF("srstart: internal I/O\n");
|
||
un->un_cmd = un->un_scmd;
|
||
un->un_count = bp->b_bcount;
|
||
un->un_blkno = bp->b_blkno;
|
||
un->un_flags = 0;
|
||
if (dsi->un_options & (SD_DVMA_RD | SD_DVMA_WR))
|
||
un->un_flags = SC_UNF_SPECIAL_DVMA;
|
||
return (1);
|
||
}
|
||
|
||
/* Process file system I/O requests */
|
||
if (bp->b_flags & B_READ) {
|
||
DPRINTF("srstart: read\n");
|
||
un->un_cmd = SC_READ;
|
||
} else {
|
||
printf("srstart: CDROM can't write\n");
|
||
un->un_cmd = SC_WRITE;
|
||
}
|
||
|
||
/* Compute absolute block location */
|
||
if ((int)lp == dsi->un_lp2) {
|
||
un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
|
||
} else {
|
||
dsi->un_lp2 = (int)lp;
|
||
dsi->un_last_cyl_size = lp->dkl_cylno * dsi->un_cyl_size;
|
||
un->un_blkno = bp->b_blkno + dsi->un_last_cyl_size;
|
||
}
|
||
/* Finish processing read/write request */
|
||
nblk = (bp->b_bcount + (SECSIZE - 1)) >> SECDIV;
|
||
un->un_count = MIN(nblk, (lp->dkl_nblk - bp->b_blkno));
|
||
un->un_flags = SC_UNF_DVMA;
|
||
un->un_count = nblk;
|
||
#ifndef FIVETWELVE
|
||
un->un_blkno /= 4; /* !! this is because each block is really 2k */
|
||
#endif FIVETWELVE
|
||
/* printf("blkno = %x count = %x\n", un->un_blkno, un->un_count); */
|
||
return (1);
|
||
}
|
||
|
||
/*
|
||
** Make a cdb for disk I/O.
|
||
*/
|
||
srmkcdb(un)
|
||
struct scsi_unit *un;
|
||
{
|
||
register struct scsi_cdb *cdb;
|
||
register struct scsi_disk *dsi;
|
||
|
||
struct uscsi_cmd *uscsi_scmd;
|
||
|
||
DPRINTF("srmkcdb:\n");
|
||
|
||
dsi = &srdisk[un->un_unit];
|
||
cdb = &un->un_cdb;
|
||
bzero((caddr_t)cdb, CDB_GROUP1);
|
||
cdb->cmd = un->un_cmd;
|
||
cdb->lun = un->un_lun;
|
||
dsi->un_timeout = 180;
|
||
un->un_dma_addr = un->un_baddr;
|
||
|
||
switch (un->un_cmd) {
|
||
case SC_READ:
|
||
DPRINTF("srmkcdb: read\n");
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
FORMG0ADDR(cdb, un->un_blkno);
|
||
FORMG0COUNT(cdb, un->un_count);
|
||
un->un_dma_count = un->un_count << SECDIV;
|
||
break;
|
||
|
||
case SC_REQUEST_SENSE:
|
||
DPRINTF("srmkcdb: request sense\n");
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
FORMG0COUNT(cdb, sizeof (struct scsi_sense));
|
||
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
||
un->un_dma_count = sizeof (struct scsi_sense);
|
||
bzero((caddr_t)(dsi->un_sense), sizeof (struct scsi_sense));
|
||
return;
|
||
|
||
case SC_SPECIAL_READ:
|
||
DPRINTF1("srmkcdb: special read blk: 0x%x\n", un->un_blkno);
|
||
un->un_cmd = cdb->cmd = SC_READ;
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
FORMG0ADDR(cdb, un->un_blkno);
|
||
FORMG0COUNT(cdb, un->un_count >> SECDIV);
|
||
un->un_dma_count = un->un_count;
|
||
break;
|
||
|
||
case SC_READ_LABEL:
|
||
DPRINTF("srmkcdb: read label\n");
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
un->un_cmd = cdb->cmd = SC_READ;
|
||
FORMG0ADDR(cdb, un->un_blkno);
|
||
FORMG0COUNT(cdb, 1);
|
||
un->un_dma_addr = (int)un->un_sbuf.b_un.b_addr - (int)DVMA;
|
||
un->un_dma_count = SECSIZE;
|
||
break;
|
||
|
||
case SC_REZERO_UNIT:
|
||
EPRINTF("srmkcdb: rezero unit\n");
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
un->un_dma_addr = 0;
|
||
un->un_dma_count = 0;
|
||
return;
|
||
|
||
case SR_START_STOP:
|
||
DPRINTF("srmkcdb: stop eject\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
un->un_dma_addr = 0;
|
||
un->un_dma_count = 0;
|
||
FORMG0ADDR(cdb, un->un_blkno);
|
||
FORMG0COUNT(cdb, un->un_count);
|
||
#ifdef DEBUGCDB
|
||
printf("START cdb g0:%x %x:%x addr:%x %x cnt:%x ctrl %x\n",
|
||
cdb->cmd, cdb->lun, cdb->tag, cdb->g0_addr1,
|
||
cdb->g0_addr0, cdb->g0_count0, cdb->g1_addr1);
|
||
#endif DEBUGCDB
|
||
break;
|
||
|
||
case SR_MEDIA_REMOV:
|
||
DPRINTF("srmkcdb: media remov\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
un->un_dma_addr = 0;
|
||
un->un_dma_count = 0;
|
||
FORMG0ADDR(cdb, un->un_blkno);
|
||
FORMG0COUNT(cdb, un->un_count);
|
||
#ifdef DEBUGCDB
|
||
printf("MEDIA cdb g0:%x %x:%x addr:%x %x cnt:%x ctrl %x\n",
|
||
cdb->cmd, cdb->lun, cdb->tag, cdb->g0_addr1,
|
||
cdb->g0_addr0, cdb->g0_count0, cdb->g1_addr1);
|
||
#endif DEBUGCDB
|
||
break;
|
||
|
||
|
||
case SC_TEST_UNIT_READY:
|
||
/* DPRINTF("srmkcdb: test unit ready\n"); */
|
||
dsi->un_timeout = 6;
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
un->un_dma_addr = 0;
|
||
un->un_dma_count = 0;
|
||
break;
|
||
|
||
case SC_INQUIRY:
|
||
DPRINTF("srmkcdb: inquiry\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
FORMG0COUNT(cdb, sizeof (struct scsi_inquiry_data));
|
||
un->un_dma_addr = (int)dsi->un_sense - (int)DVMA;
|
||
un->un_dma_count = sizeof (struct scsi_inquiry_data);
|
||
bzero((caddr_t)(dsi->un_sense), sizeof (struct scsi_inquiry_data));
|
||
break;
|
||
|
||
#ifdef OLDCODE
|
||
case SC_MODE_SELECT:
|
||
EPRINTF("srmkcdb: mode select\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
FORMG0COUNT(cdb, un->un_count);
|
||
un->un_dma_count = un->un_count;
|
||
un->un_dma_addr = un->un_baddr;
|
||
if (un->un_blkno & 0x80) {
|
||
EPRINTF("srmkcdb: savable mode select\n");
|
||
cdb->tag = 1; /* Save parameters */
|
||
}
|
||
break;
|
||
#endif OLDCODE
|
||
|
||
case SC_MODE_SENSE:
|
||
EPRINTF("srmkcdb: mode sense\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
FORMG0COUNT(cdb, un->un_count);
|
||
cdb->g0_addr1 = un->un_blkno;
|
||
un->un_dma_count = un->un_count;
|
||
un->un_dma_addr = un->un_baddr;
|
||
break;
|
||
|
||
/*
|
||
* case CDROM IOCTL COMMANDS
|
||
*/
|
||
default:
|
||
if ((uscsi_scmd = (struct uscsi_cmd *)(un->un_scratch_addr)) !=0){
|
||
if (uscsi_scmd->uscsi_flags & USCSI_READ) {
|
||
un->un_flags |= (SC_UNF_RECV_DATA | dsi->un_flags);
|
||
} else {
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
}
|
||
|
||
DPRINTF1("srmkcdb: cdb cmd is 0x%x\n",
|
||
(u_char)uscsi_scmd->uscsi_cdb[0]);
|
||
/* bcopy((caddr_t)uscsi_scmd->uscsi_cdb, cdb,
|
||
uscsi_scmd->uscsi_cdblen); */
|
||
sr_makecom_all((char *)uscsi_scmd->uscsi_cdb,
|
||
cdb);
|
||
if (uscsi_scmd->uscsi_buflen == 0) {
|
||
un->un_dma_addr = 0;
|
||
un->un_dma_count = 0;
|
||
} else {
|
||
un->un_dma_addr = (caddr_t)uscsi_scmd->
|
||
uscsi_bufaddr - (caddr_t)DVMA;
|
||
if (un->un_dma_addr & 0x3)
|
||
printf("dma_addr %x\n", un->un_dma_addr);
|
||
un->un_dma_count = uscsi_scmd->uscsi_buflen;
|
||
}
|
||
un->un_cmd_len = uscsi_scmd->uscsi_cdblen;
|
||
break;
|
||
} else {
|
||
EPRINTF("srmkcdb: unknown command\n");
|
||
dsi->un_timeout = 6;
|
||
un->un_flags = (un->un_flags & ~SC_UNF_RECV_DATA) |
|
||
dsi->un_flags;
|
||
break;
|
||
}
|
||
}
|
||
dsi->un_last_cmd = un->un_cmd;
|
||
return;
|
||
}
|
||
|
||
|
||
/*
|
||
** This routine handles controller interrupts.
|
||
** It is always called at disk interrupt priority.
|
||
*/
|
||
typedef enum srintr_error_resolution {
|
||
real_error, /* Hard error */
|
||
psuedo_error, /* What looked like an error is actually OK */
|
||
more_processing /* Recoverable error */
|
||
} srintr_error_resolution;
|
||
|
||
srintr_error_resolution srerror();
|
||
|
||
|
||
srintr(c, resid, error)
|
||
register struct scsi_ctlr *c;
|
||
u_int resid;
|
||
int error;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
register struct buf *bp;
|
||
register struct mb_device *md;
|
||
int status = 0;
|
||
int error_code = 0;
|
||
u_char severe = DK_NOERROR;
|
||
|
||
DPRINTF("srintr:\n");
|
||
un = c->c_un;
|
||
md = un->un_md;
|
||
bp = md->md_utab.b_forw;
|
||
if (bp == NULL) {
|
||
printf("srintr: bp = NULL\n");
|
||
return;
|
||
}
|
||
|
||
dsi = &srdisk[SRUNIT(bp->b_dev)];
|
||
dsi->un_timeout = 0; /* Disable time-outs */
|
||
|
||
if (md->md_dk >= 0) {
|
||
dk_busy &= ~(1 << md->md_dk);
|
||
}
|
||
|
||
/*
|
||
* Check for error or special operation states and process.
|
||
* Otherwise, it was a normal I/O command which was successful.
|
||
*/
|
||
if (dsi->un_openf < OPEN || error || resid) {
|
||
/*
|
||
** Special processing for SCSI bus failures.
|
||
*/
|
||
if (error == SE_FATAL) {
|
||
if (dsi->un_openf == OPEN) {
|
||
printf("sr%d: scsi bus failure\n", SRNUM(un));
|
||
}
|
||
dsi->un_retries = dsi->un_restores = 0;
|
||
dsi->un_err_severe = DK_FATAL;
|
||
un->un_present = 0;
|
||
dsi->un_openf = CLOSED;
|
||
bp->b_flags |= B_ERROR;
|
||
goto SRINTR_WRAPUP;
|
||
}
|
||
|
||
/*
|
||
** Opening disk, check if command failed. If it failed
|
||
** close device. Otherwise, it's open.
|
||
*/
|
||
if (dsi->un_openf == OPENING) {
|
||
if (error == SE_NO_ERROR) {
|
||
dsi->un_openf = OPEN;
|
||
goto SRINTR_SUCCESS;
|
||
} else if (error == SE_RETRYABLE) {
|
||
/* DPRINTF("srintr: open failed\n"); */
|
||
dsi->un_openf = OPEN_FAILED;
|
||
bp->b_flags |= B_ERROR;
|
||
goto SRINTR_WRAPUP;
|
||
} else {
|
||
EPRINTF("srintr: hardware failure\n");
|
||
dsi->un_openf = CLOSED;
|
||
bp->b_flags |= B_ERROR;
|
||
goto SRINTR_WRAPUP;
|
||
}
|
||
}
|
||
|
||
/*
|
||
** Rezero for failed command done, retry failed command
|
||
*/
|
||
if ((dsi->un_openf == RETRYING) &&
|
||
(un->un_cdb.cmd == SC_REZERO_UNIT)) {
|
||
|
||
if (error)
|
||
DPRINTF1("sr%d: rezero failed\n", SRNUM(un));
|
||
|
||
DPRINTF("srintr: rezero done\n");
|
||
un->un_flags &= ~SC_UNF_GET_SENSE;
|
||
un->un_cdb = un->un_saved_cmd.saved_cdb;
|
||
un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
|
||
un->un_dma_count = un->un_saved_cmd.saved_dma_count;
|
||
un->un_cmd = dsi->un_last_cmd;
|
||
srmkcdb(un);
|
||
(*c->c_ss->scs_cmd)(c, un, 1);
|
||
return;
|
||
}
|
||
|
||
|
||
/*
|
||
* Command failed, need to run request sense command.
|
||
*/
|
||
if ((dsi->un_openf == OPEN) && error) {
|
||
srintr_sense(dsi, un, resid);
|
||
return;
|
||
}
|
||
|
||
/*
|
||
* Request sense command done, restore failed command.
|
||
*/
|
||
if (dsi->un_openf == SENSING) {
|
||
DPRINTF("srintr: restoring sense\n");
|
||
srintr_ran_sense(un, dsi, &resid);
|
||
}
|
||
|
||
/*
|
||
* Reassign done, restore original state
|
||
*/
|
||
if (dsi->un_openf == MAPPING) {
|
||
DPRINTF("srintr: restoring state\n");
|
||
srintr_ran_reassign(un, dsi, &resid);
|
||
}
|
||
|
||
EPRINTF2("srintr: retries= %d restores= %d ",
|
||
dsi->un_retries, dsi->un_restores);
|
||
EPRINTF1("total= %d\n", dsi->un_total_errors);
|
||
if ((dsi->un_openf == RETRYING) && (error == 0)) {
|
||
|
||
EPRINTF("srintr: ok\n\n");
|
||
dsi->un_openf = OPEN;
|
||
dsi->un_retries = dsi->un_restores = 0;
|
||
dsi->un_err_severe = DK_RECOVERED;
|
||
goto SRINTR_SUCCESS;
|
||
}
|
||
|
||
/*
|
||
* Process all other errors here
|
||
*/
|
||
EPRINTF2("srintr: processing error, error= %x chk= %x",
|
||
error, un->un_scb.chk);
|
||
EPRINTF1(" busy= %x", un->un_scb.busy);
|
||
EPRINTF2(" resid= %d (%d)\n", resid, un->un_dma_count);
|
||
switch (srerror(c, un, dsi, bp, resid, error)) {
|
||
case real_error:
|
||
/* This error is FATAL ! */
|
||
DPRINTF("srintr: real error\n");
|
||
dsi->un_retries = dsi->un_restores = 0;
|
||
bp->b_flags |= B_ERROR;
|
||
goto SRINTR_WRAPUP;
|
||
|
||
case psuedo_error:
|
||
/* A psuedo-error: soft error reported by ctlr */
|
||
DPRINTF("srintr: psuedo error\n");
|
||
status = dsi->un_status;
|
||
error_code = dsi->un_err_code;
|
||
severe = dsi->un_err_severe;
|
||
dsi->un_retries = dsi->un_restores = 0;
|
||
goto SRINTR_SUCCESS;
|
||
|
||
case more_processing:
|
||
/* real error requiring error recovery */
|
||
DPRINTF("stintr: more processing\n");
|
||
return;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
* Handle successful Transfer. Also, take care of ACB-4000
|
||
* seek error problem by doing single sector I/O.
|
||
*/
|
||
SRINTR_SUCCESS:
|
||
dsi->un_status = status;
|
||
dsi->un_err_code = error_code;
|
||
dsi->un_err_severe = severe;
|
||
|
||
if (dsi->un_sec_left) {
|
||
EPRINTF("srintr: single sector writes\n");
|
||
dsi->un_sec_left--;
|
||
un->un_baddr += SECSIZE;
|
||
un->un_blkno++;
|
||
srmkcdb(un);
|
||
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0)
|
||
printf("sr%d: single sector I/O failed\n", SRNUM(un));
|
||
}
|
||
|
||
|
||
/*
|
||
* Handle I/O request completion (both sucessful and failed).
|
||
*/
|
||
SRINTR_WRAPUP:
|
||
if (bp == &un->un_sbuf &&
|
||
((un->un_flags & SC_UNF_DVMA) == 0)) {
|
||
bp->b_resid = 0;
|
||
if (un->un_flags & SC_UNF_SPECIAL_DVMA)
|
||
mbudone(un->un_md);
|
||
else
|
||
(*c->c_ss->scs_done)(un->un_md);
|
||
|
||
} else {
|
||
bp->b_resid = bp->b_bcount - (un->un_count << SECDIV);
|
||
mbudone(un->un_md);
|
||
un->un_flags &= ~SC_UNF_DVMA;
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** Disk error decoder/handler.
|
||
*/
|
||
static srintr_error_resolution
|
||
srerror(c, un, dsi, bp, resid, error)
|
||
register struct scsi_ctlr *c;
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
struct buf *bp;
|
||
u_int resid;
|
||
int error;
|
||
{
|
||
register struct scsi_ext_sense *ssr;
|
||
|
||
ssr = (struct scsi_ext_sense *)dsi->un_sense;
|
||
/* DPRINTF("srerror:\n"); */
|
||
|
||
/*
|
||
* Special processing for driver command timeout errors.
|
||
*/
|
||
if (error == SE_TIMEOUT) {
|
||
EPRINTF("srerror: command timeout error\n");
|
||
dsi->un_status = SC_TIMEOUT;
|
||
goto SRERROR_RETRY;
|
||
}
|
||
|
||
/*
|
||
* Check for Adaptec ACB-4000 seek error problem. If found,
|
||
* transfer data one sector at a time.
|
||
*/
|
||
if (dsi->un_ctype == CTYPE_ACB4000 && un->un_scb.chk &&
|
||
ssr->error_code == 15 && un->un_count > 1) {
|
||
EPRINTF("srerror: seek error\n");
|
||
dsi->un_sec_left = un->un_count - 1;
|
||
un->un_count = 1;
|
||
srmkcdb(un);
|
||
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0) {
|
||
printf("sr%d: srerror: single sector I/O failed\n",
|
||
SRNUM(un));
|
||
return (real_error);
|
||
}
|
||
return (more_processing);
|
||
}
|
||
|
||
/*
|
||
* Check for various check condition errors.
|
||
*/
|
||
dsi->un_total_errors++;
|
||
if (un->un_scb.chk) {
|
||
|
||
switch (ssr->key) {
|
||
case SC_RECOVERABLE_ERROR:
|
||
sr_fix_block(c, dsi, un, resid, SE_RETRYABLE);
|
||
dsi->un_err_severe = DK_CORRECTED;
|
||
if (sr_retry == 0) {
|
||
srerrmsg(un, bp, "recoverable");
|
||
}
|
||
return (psuedo_error);
|
||
|
||
case SC_MEDIUM_ERROR:
|
||
EPRINTF("srerror: media error\n");
|
||
/* sr_fix_block(c, dsi, un, resid, SE_HARD_ERROR); */
|
||
goto SRERROR_RETRY;
|
||
|
||
case SC_HARDWARE_ERROR:
|
||
EPRINTF("srerror: hardware error\n");
|
||
goto SRERROR_RETRY;
|
||
|
||
/* XXX this also occurs on an unexpected media removal with code 28 */
|
||
case SC_UNIT_ATTENTION:
|
||
EPRINTF("srerror: unit attention error\n");
|
||
goto SRERROR_RETRY;
|
||
|
||
case SC_NOT_READY:
|
||
EPRINTF("srerror: not ready\n");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
case SC_ILLEGAL_REQUEST:
|
||
EPRINTF("srerror: illegal request\n");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
case SC_VOLUME_OVERFLOW:
|
||
EPRINTF("srerror: volume overflow\n");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
case SC_WRITE_PROTECT:
|
||
EPRINTF("srerror: write protected\n");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
case SC_BLANK_CHECK:
|
||
EPRINTF("srerror: blank check\n");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
default:
|
||
/*
|
||
* Undecoded sense key. Try retries and hope
|
||
* that will fix the problem. Otherwise, we're
|
||
* dead.
|
||
*/
|
||
EPRINTF("srerror: undecoded sense key error\n");
|
||
SR_PRINT_CMD(un);
|
||
dsi->un_err_severe = DK_FATAL;
|
||
goto SRERROR_RETRY;
|
||
}
|
||
|
||
/*
|
||
* Process busy error. Try retries and hope that
|
||
* it'll be ready soon.
|
||
*/
|
||
} else if (un->un_scb.busy && !un->un_scb.is) {
|
||
EPRINTF1("sr%d: srerror: busy error\n", SRNUM(un));
|
||
SR_PRINT_CMD(un);
|
||
goto SRERROR_RETRY;
|
||
|
||
/*
|
||
* Process reservation error. Abort operation.
|
||
*/
|
||
} else if (un->un_scb.busy && un->un_scb.is) {
|
||
EPRINTF1("sr%d: srerror: reservation conflict error\n",
|
||
SRNUM(un));
|
||
SR_PRINT_CMD(un);
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
|
||
/*
|
||
* Have left over residue data from last command.
|
||
* Do retries and hope this fixes it...
|
||
*/
|
||
} else if (resid != 0) {
|
||
EPRINTF1("srerror: residue error, residue= %d\n", resid);
|
||
SR_PRINT_CMD(un);
|
||
goto SRERROR_RETRY;
|
||
|
||
/*
|
||
* Have an unknown error. Don't know what went wrong.
|
||
* Do retries and hope this fixes it...
|
||
*/
|
||
} else {
|
||
EPRINTF("srerror: unknown error\n");
|
||
SR_PRINT_CMD(un);
|
||
dsi->un_err_severe = DK_FATAL;
|
||
goto SRERROR_RETRY;
|
||
}
|
||
|
||
/*
|
||
* Process command retries and rezeros here.
|
||
* Note, for on-line formatting, normal error
|
||
* recovery is inhibited.
|
||
*/
|
||
SRERROR_RETRY:
|
||
if (dsi->un_options & SD_NORETRY) {
|
||
EPRINTF("srerror: error recovery disabled\n");
|
||
srerrmsg(un, bp, "failed, no retries");
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
}
|
||
|
||
/*
|
||
* Command failed, retry it.
|
||
*/
|
||
if (dsi->un_retries++ < sr_max_retries) {
|
||
if (((dsi->un_retries + dsi->un_restores) > sr_retry) ||
|
||
(dsi->un_restores != 0)) {
|
||
srerrmsg(un, bp, "retry");
|
||
}
|
||
|
||
dsi->un_openf = RETRYING;
|
||
srmkcdb(un);
|
||
if ((*c->c_ss->scs_cmd)(c, un, 1) != 0) {
|
||
printf("sr%d: srerror: retry failed\n", SRNUM(un));
|
||
return (real_error);
|
||
}
|
||
return (more_processing);
|
||
|
||
/*
|
||
* Retries exhausted, try restore
|
||
*/
|
||
} else if (++dsi->un_restores < sr_max_restores) {
|
||
if (dsi->un_restores > sr_restores)
|
||
srerrmsg(un, bp, "restore");
|
||
|
||
/*
|
||
* Save away old command state.
|
||
*/
|
||
un->un_saved_cmd.saved_cdb = un->un_cdb;
|
||
un->un_saved_cmd.saved_dma_addr = un->un_dma_addr;
|
||
un->un_saved_cmd.saved_dma_count = un->un_dma_count;
|
||
dsi->un_openf = RETRYING;
|
||
dsi->un_retries = 0;
|
||
un->un_cmd = SC_REZERO_UNIT;
|
||
srmkcdb(un);
|
||
(void) (*c->c_ss->scs_cmd)(c, un, 1);
|
||
return (more_processing);
|
||
|
||
/*
|
||
* Restores and retries exhausted, die!
|
||
*/
|
||
} else {
|
||
/* complete failure */
|
||
srerrmsg(un, bp, "failed");
|
||
dsi->un_openf = OPEN;
|
||
dsi->un_retries = 0;
|
||
dsi->un_restores = 0;
|
||
dsi->un_err_severe = DK_FATAL;
|
||
return (real_error);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** Command failed, need to run a request sense command to determine why.
|
||
*/
|
||
static
|
||
srintr_sense(dsi, un, resid)
|
||
register struct scsi_disk *dsi;
|
||
register struct scsi_unit *un;
|
||
u_int resid;
|
||
{
|
||
|
||
/*
|
||
* Save away old command state.
|
||
*/
|
||
un->un_saved_cmd.saved_scb = un->un_scb;
|
||
un->un_saved_cmd.saved_cdb = un->un_cdb;
|
||
un->un_saved_cmd.saved_resid = resid;
|
||
un->un_saved_cmd.saved_dma_addr = un->un_dma_addr;
|
||
un->un_saved_cmd.saved_dma_count = un->un_dma_count;
|
||
/*
|
||
* Note that s?start will call srmkcdb, which
|
||
* will notice that the flag is set and not do
|
||
* the copy of the cdb, doing a request sense
|
||
* rather than the normal command.
|
||
*/
|
||
DPRINTF("srintr: getting sense\n");
|
||
un->un_flags |= SC_UNF_GET_SENSE;
|
||
dsi->un_openf = SENSING;
|
||
un->un_cmd = SC_REQUEST_SENSE;
|
||
(*un->un_c->c_ss->scs_go)(un->un_md);
|
||
}
|
||
|
||
|
||
/*
|
||
** Cleanup after running request sense command to see why the real
|
||
** command failed.
|
||
*/
|
||
static
|
||
srintr_ran_sense(un, dsi, resid_ptr)
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
u_int *resid_ptr;
|
||
{
|
||
register struct scsi_ext_sense *ssr;
|
||
|
||
/*
|
||
* Check if request sense command failed. This should
|
||
* never happen!
|
||
*/
|
||
un->un_flags &= ~SC_UNF_GET_SENSE;
|
||
dsi->un_openf = OPEN;
|
||
if (un->un_scb.chk) {
|
||
printf("sr%d: request sense failed\n",
|
||
SRNUM(un));
|
||
}
|
||
|
||
un->un_scb = un->un_saved_cmd.saved_scb;
|
||
un->un_cdb = un->un_saved_cmd.saved_cdb;
|
||
*resid_ptr = un->un_saved_cmd.saved_resid;
|
||
un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
|
||
un->un_dma_count = un->un_saved_cmd.saved_dma_count;
|
||
un->un_flags &= ~SC_UNF_GET_SENSE;
|
||
un->un_cmd = dsi->un_last_cmd;
|
||
dsi->un_openf = OPEN;
|
||
|
||
/*
|
||
* Special processing for Adaptec ACB-4000 disk controller.
|
||
*/
|
||
ssr = (struct scsi_ext_sense *)dsi->un_sense;
|
||
if (dsi->un_ctype == CTYPE_ACB4000 || ssr->type != 0x70) {
|
||
srintr_adaptec(dsi);
|
||
}
|
||
|
||
/*
|
||
* Log error information.
|
||
*/
|
||
dsi->un_err_resid = *resid_ptr;
|
||
dsi->un_status = ssr->key;
|
||
dsi->un_err_code = ssr->error_code;
|
||
|
||
dsi->un_err_blkno = (ssr->info_1 << 24) | (ssr->info_2 << 16) |
|
||
(ssr->info_3 << 8) | ssr->info_4;
|
||
if (dsi->un_err_blkno == 0 || !(ssr->adr_val)) {
|
||
/* No supplied block number, use original value */
|
||
EPRINTF("srintr_ran_sense: synthesizing block number\n");
|
||
dsi->un_err_blkno = un->un_blkno;
|
||
}
|
||
|
||
#ifdef SRDEBUG
|
||
/* dump sense info on screen */
|
||
if (sr_debug > 1) {
|
||
sr_error_message(un, dsi);
|
||
printf("\n");
|
||
}
|
||
#endif SRDEBUG
|
||
}
|
||
|
||
|
||
/*
|
||
** Cleanup after running request sense command to see why the real
|
||
** command failed.
|
||
*/
|
||
/*ARGSUSED*/
|
||
static
|
||
sr_fix_block(c, dsi, un, resid, err_type)
|
||
register struct scsi_ctlr *c;
|
||
register struct scsi_disk *dsi;
|
||
register struct scsi_unit *un;
|
||
u_int resid;
|
||
short err_type;
|
||
{
|
||
register int i;
|
||
|
||
if (err_type == SE_RETRYABLE) {
|
||
if (dsi->un_bad_index == 0)
|
||
goto SR_SAVE;
|
||
|
||
/*
|
||
* Search bad block log for match. If found,
|
||
* increment counter. If reporting theshold passed,
|
||
* inform user. If remap threshold passed, reassign
|
||
* block. Otherwise, just log it.
|
||
*/
|
||
for (i = 0; i < dsi->un_bad_index; i++) {
|
||
|
||
/* Didn't find it, try next entry */
|
||
if (dsi->un_bad[i].block != dsi->un_err_blkno)
|
||
continue;
|
||
|
||
/* Found it, check if we need to reassign the block */
|
||
if ((dsi->un_bad[i].retries)++ >= sr_max_fails) {
|
||
|
||
printf("sr%d: block 0x%x needs mapping\n",
|
||
SRNUM(un), dsi->un_bad[i].block);
|
||
err_type = SE_FATAL;
|
||
}
|
||
goto SR_FIX_BLK;
|
||
}
|
||
SR_SAVE:
|
||
if (dsi->un_bad_index == NBAD) {
|
||
EPRINTF1("sr%d: no storage for error logging\n",
|
||
SRNUM(un));
|
||
return;
|
||
}
|
||
EPRINTF("si_fix_block: logging marginal block\n");
|
||
i = dsi->un_bad_index++;
|
||
dsi->un_bad[i].block = dsi->un_err_blkno;
|
||
dsi->un_bad[i].retries = 1;
|
||
}
|
||
|
||
|
||
SR_FIX_BLK:
|
||
/* Check if need to warn user */
|
||
if (dsi->un_bad[i].retries > sr_fails) {
|
||
printf("sr%d: warning, abs. block %d has failed %d times\n",
|
||
SRNUM(un), dsi->un_bad[i].block,
|
||
dsi->un_bad[i].retries);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** Cleanup after running reassign block command.
|
||
*/
|
||
/*ARGSUSED*/
|
||
static
|
||
srintr_ran_reassign(un, dsi, resid_ptr)
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
register u_int *resid_ptr;
|
||
{
|
||
/*
|
||
* Check if reassign command failed.
|
||
*/
|
||
un->un_flags &= ~SC_UNF_GET_SENSE;
|
||
dsi->un_openf = OPEN;
|
||
if (un->un_scb.chk)
|
||
printf("sr%d: reassign block failed\n", SRNUM(un));
|
||
|
||
un->un_scb = un->un_saved_cmd.saved_scb;
|
||
un->un_cdb = un->un_saved_cmd.saved_cdb;
|
||
*resid_ptr = un->un_saved_cmd.saved_resid;
|
||
un->un_dma_addr = un->un_saved_cmd.saved_dma_addr;
|
||
un->un_dma_count = un->un_saved_cmd.saved_dma_count;
|
||
un->un_flags &= ~SC_UNF_GET_SENSE;
|
||
un->un_cmd = dsi->un_last_cmd;
|
||
dsi->un_openf = OPEN;
|
||
}
|
||
|
||
|
||
/*
|
||
** This routine performs raw read operations. It is called from the
|
||
** device switch at normal priority. It uses a per-unit buffer for the
|
||
** operation.
|
||
*/
|
||
/*ARGSUSED*/
|
||
srread(dev, uio)
|
||
dev_t dev;
|
||
struct uio *uio;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register int unit;
|
||
|
||
DPRINTF("srread:\n");
|
||
unit = SRUNIT(dev);
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("srread: invalid unit\n");
|
||
return (ENXIO);
|
||
}
|
||
if ((uio->uio_fmode & FSETBLK) == 0 &&
|
||
(uio->uio_offset & (DEV_BSIZE - 1)) != 0) {
|
||
EPRINTF1("srread: block address not modulo %d\n",
|
||
DEV_BSIZE);
|
||
return (EINVAL);
|
||
}
|
||
if (uio->uio_iov->iov_len & (DEV_BSIZE - 1)) {
|
||
EPRINTF1("srread: block length not modulo %d\n",
|
||
DEV_BSIZE);
|
||
return (EINVAL);
|
||
}
|
||
un = &srunits[unit];
|
||
return (physio(srstrategy, &un->un_rbuf, dev, B_READ, minphys, uio));
|
||
}
|
||
|
||
#ifdef NOTDEF
|
||
|
||
/*
|
||
** This routine performs raw write operations. It is called from the
|
||
** device switch at normal priority. It uses a per-unit buffer for the
|
||
** operation.
|
||
*/
|
||
srwrite(dev, uio)
|
||
dev_t dev;
|
||
struct uio *uio;
|
||
{
|
||
register struct scsi_unit *un;
|
||
register int unit;
|
||
|
||
DPRINTF("srwrite:\n");
|
||
unit = SRUNIT(dev);
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("srwrite: invalid unit\n");
|
||
return (ENXIO);
|
||
}
|
||
if ((uio->uio_fmode & FSETBLK) == 0 &&
|
||
(uio->uio_offset & (DEV_BSIZE - 1)) != 0)
|
||
EPRINTF1("srwrite: block address not modulo %d\n",
|
||
DEV_BSIZE);
|
||
return (EINVAL);
|
||
}
|
||
if (uio->uio_iov->iov_len & (DEV_BSIZE - 1)) {
|
||
EPRINTF1("srwrite: block length not modulo %d\n",
|
||
DEV_BSIZE);
|
||
return (EINVAL);
|
||
}
|
||
un = &srunits[unit];
|
||
return (physio(srstrategy, &un->un_rbuf, dev, B_WRITE, minphys, uio));
|
||
}
|
||
#endif NOTDEF
|
||
|
||
/*
|
||
** This routine implements the ioctl calls. It is called
|
||
** from the device switch at normal priority.
|
||
*/
|
||
/*ARGSUSED*/
|
||
srioctl(dev, cmd, data, flag)
|
||
dev_t dev;
|
||
register int cmd;
|
||
register caddr_t data;
|
||
int flag;
|
||
{
|
||
extern char *strcpy();
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
register struct dk_info *info;
|
||
register struct dk_diag *diag;
|
||
register int unit;
|
||
short sony;
|
||
|
||
DPRINTF("srioctl:\n");
|
||
unit = SRUNIT(dev);
|
||
if (unit >= ncdrom) {
|
||
EPRINTF("srioctl: invalid unit\n");
|
||
return (ENXIO);
|
||
}
|
||
if (!(un = &srunits[unit]) ||
|
||
!(dsi = &srdisk[unit])) {
|
||
EPRINTF("srioctl: invalid open state\n");
|
||
return (ENXIO);
|
||
};
|
||
|
||
sony = (bcmp((caddr_t)sr_inq[unit].cdrom_vid, "SONY", 4) == 0);
|
||
|
||
switch (cmd) {
|
||
|
||
/*
|
||
** Return info concerning the controller.
|
||
*/
|
||
case DKIOCINFO:
|
||
DPRINTF("srioctl: get info\n");
|
||
info = (struct dk_info *)data;
|
||
info->dki_ctlr = getdevaddr(un->un_mc->mc_addr);
|
||
info->dki_unit = un->un_md->md_slave;
|
||
|
||
switch (dsi->un_ctype) {
|
||
case CTYPE_MD21:
|
||
info->dki_ctype = DKC_MD21;
|
||
break;
|
||
case CTYPE_ACB4000:
|
||
info->dki_ctype = DKC_ACB4000;
|
||
break;
|
||
default:
|
||
info->dki_ctype = DKC_MD21;
|
||
break;
|
||
}
|
||
info->dki_flags = DKI_FMTVOL;
|
||
return (0);
|
||
|
||
/*
|
||
* Return the geometry of the specified unit.
|
||
*/
|
||
case DKIOCGGEOM:
|
||
DPRINTF("srioctl: get geometry\n");
|
||
*(struct dk_geom *)data = dsi->un_g;
|
||
return (0);
|
||
|
||
/*
|
||
* Get error status from last command.
|
||
*/
|
||
case DKIOCGDIAG:
|
||
EPRINTF("srioctl: get error status\n");
|
||
diag = (struct dk_diag *) data;
|
||
diag->dkd_errcmd = dsi->un_last_cmd;
|
||
diag->dkd_errsect = dsi->un_err_blkno;
|
||
diag->dkd_errno = dsi->un_status;
|
||
/* diag->dkd_errno = dsi->un_err_code; */
|
||
dsi->un_last_cmd = 0; /* Reset */
|
||
dsi->un_err_blkno = 0;
|
||
dsi->un_err_code = 0;
|
||
dsi->un_err_severe = 0;
|
||
return (0);
|
||
|
||
/*
|
||
* Run a generic command.
|
||
*/
|
||
case DKIOCSCMD:
|
||
/* DPRINTF("srioctl: run special command\n"); */
|
||
return (srioctl_cmd(dev, data, 0));
|
||
|
||
case CDROMPAUSE:
|
||
if (sony) {
|
||
return (sr_pause_resume(dev, (caddr_t)1));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMRESUME:
|
||
if (sony) {
|
||
return (sr_pause_resume(dev, (caddr_t)0));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMPLAYMSF:
|
||
if (sony) {
|
||
return (sr_play_msf(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMPLAYTRKIND:
|
||
if (sony) {
|
||
return (sr_play_trkind(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMREADTOCHDR:
|
||
if (sony) {
|
||
return (sr_read_tochdr(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMREADTOCENTRY:
|
||
if (sony) {
|
||
return (sr_read_tocentry(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMSTOP:
|
||
if (sony) {
|
||
return (sr_start_stop(dev, (caddr_t)0));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMSTART:
|
||
if (sony) {
|
||
return (sr_start_stop(dev, (caddr_t)1));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case FDKEJECT:
|
||
case CDROMEJECT:
|
||
if (sony) {
|
||
return (sr_eject(dev));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMVOLCTRL:
|
||
if (sony) {
|
||
return (sr_volume_ctrl(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
case CDROMSUBCHNL:
|
||
if (sony) {
|
||
return (sr_read_subchannel(dev, data));
|
||
} else {
|
||
return (ENOTTY);
|
||
}
|
||
|
||
/*
|
||
* this driver does not support these ioctl command for now.
|
||
* will support it in the later version.
|
||
*/
|
||
|
||
case CDROMREADMODE2:
|
||
case CDROMREADMODE1:
|
||
return (EIO);
|
||
|
||
/*
|
||
* Handle unknown ioctls here.
|
||
*/
|
||
default:
|
||
EPRINTF("srioctl: unknown ioctl\n");
|
||
return (ENOTTY);
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** Run a command for srioctl.
|
||
*/
|
||
|
||
/*ARGSUSED*/
|
||
static int
|
||
srioctl_cmd(dev, data, flag)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
int flag;
|
||
{
|
||
/* register struct dk_cmd *com; */
|
||
register struct scsi_unit *un;
|
||
register struct uscsi_cmd *scmd;
|
||
char *cdb;
|
||
int blkno;
|
||
int err, options;
|
||
int s;
|
||
int unit;
|
||
|
||
DPRINTF("srioctl_cmd:\n");
|
||
unit = SRUNIT(dev);
|
||
un = &srunits[unit];
|
||
|
||
/* com = (struct dk_cmd *)data; */
|
||
scmd = (struct uscsi_cmd *)data;
|
||
cdb = scmd->uscsi_cdb;
|
||
blkno = 0;
|
||
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 1) {
|
||
/* printf("srioctl_cmd: cmd= %x blk= %x cnt= %x ",
|
||
com->dkc_cmd, com->dkc_blkno, com->dkc_secnt);
|
||
printf("buf addr= %x buflen= 0x%x\n",
|
||
com->dkc_bufaddr, com->dkc_buflen); */
|
||
printf("srioctl_cmd: cmd= %x blk= %x cnt= %x ",
|
||
cdb_cmd(cdb), blkno, scmd->uscsi_buflen);
|
||
printf("buf addr= %x buflen= 0x%x\n",
|
||
scmd->uscsi_bufaddr, scmd->uscsi_buflen);
|
||
}
|
||
#endif SRDEBUG
|
||
/*
|
||
* Set options.
|
||
*/
|
||
options = 0;
|
||
if (scmd->uscsi_flags & USCSI_SILENT) {
|
||
options |= SD_SILENT;
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 0)
|
||
options &= ~SD_SILENT;
|
||
#endif SRDEBUG
|
||
}
|
||
if (scmd->uscsi_flags & USCSI_ISOLATE)
|
||
options |= SD_NORETRY;
|
||
|
||
/*
|
||
* Process the special ioctl command.
|
||
*/
|
||
switch (cdb_cmd(cdb)) {
|
||
case SC_READ:
|
||
DPRINTF("srioctl_cmd: read\n");
|
||
options |= SD_DVMA_RD;
|
||
blkno = cdb0_blkno(cdb);
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
err = srcmd(dev, SC_SPECIAL_READ, blkno,
|
||
(int)scmd->uscsi_buflen,
|
||
(caddr_t)scmd->uscsi_bufaddr, options, (caddr_t)0);
|
||
(void) splx(s);
|
||
break;
|
||
|
||
#ifdef OLDCODE
|
||
case SC_MODE_SELECT:
|
||
EPRINTF("srioctl_cmd: mode select\n");
|
||
options |= SD_DVMA_WR;
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
blkno = cdb0_blkno(cdb);
|
||
err = srcmd(dev, SC_MODE_SELECT, blkno,
|
||
(int)scmd->uscsi_buflen,
|
||
(caddr_t)scmd->uscsi_bufaddr,
|
||
options, (caddr_t)0);
|
||
(void) splx(s);
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 2) {
|
||
sr_print_buffer((u_char *)scmd->uscsi_bufaddr,
|
||
scmd->uscsi_buflen);
|
||
printf("\n");
|
||
}
|
||
#endif SRDEBUG
|
||
break;
|
||
#endif OLDCODE
|
||
|
||
case SC_MODE_SENSE:
|
||
EPRINTF("srioctl_cmd: mode sense\n");
|
||
options |= SD_DVMA_RD;
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
blkno = cdb0_blkno(cdb);
|
||
err = srcmd(dev, SC_MODE_SENSE, blkno,
|
||
(int)scmd->uscsi_buflen,
|
||
(caddr_t)scmd->uscsi_bufaddr,
|
||
options, (caddr_t)0);
|
||
(void) splx(s);
|
||
#ifdef SRDEBUG
|
||
if (sr_debug > 2) {
|
||
sr_print_buffer((u_char *)scmd->uscsi_bufaddr,
|
||
(int)scmd->uscsi_buflen);
|
||
printf("\n");
|
||
}
|
||
#endif SRDEBUG
|
||
break;
|
||
|
||
default:
|
||
EPRINTF1("srioctl_cmd: command is 0x%x\n",
|
||
cdb_cmd(cdb));
|
||
s = splr(pritospl(un->un_mc->mc_intpri));
|
||
err = srcmd(dev, (int)cdb_cmd(cdb), 0, 0, (caddr_t)0, options,
|
||
(caddr_t)scmd);
|
||
(void) splx(s);
|
||
break;
|
||
|
||
/* default:
|
||
EPRINTF1("srioctl_cmd: unknown command %x\n", com->dkc_cmd);
|
||
return (EINVAL); */
|
||
/* break; */
|
||
}
|
||
if (err) {
|
||
EPRINTF("srioctl_cmd: ioctl cmd failed\n");
|
||
return (EIO);
|
||
}
|
||
return (0);
|
||
}
|
||
|
||
/*
|
||
** This routine locks the cdrom door and prevent medium removal.
|
||
*/
|
||
|
||
static int
|
||
sr_medium_removal(dev, flag)
|
||
dev_t dev;
|
||
int flag;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
int rtn;
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(6)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
com->uscsi_bufaddr = 0;
|
||
com->uscsi_buflen = 0;
|
||
cdb[0] = SCMD_DOORLOCK;
|
||
cdb[1] = 0;
|
||
cdb[2] = 0;
|
||
cdb[3] = 0;
|
||
cdb[4] = flag;
|
||
cdb[5] = 0;
|
||
com->uscsi_flags = DK_DIAGNOSE|DK_SILENT;
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 6;
|
||
|
||
rtn = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 6);
|
||
|
||
return (rtn);
|
||
|
||
}
|
||
|
||
/*
|
||
** This routine does a pause or resume to the cdrom player. Effects
|
||
** only audio play operation.
|
||
*/
|
||
static int
|
||
sr_pause_resume(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
int flag;
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
int rtn;
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
|
||
flag = (int)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_PAUSE_RESUME;
|
||
cdb[8] = (flag == 0) ? 1 : 0;
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = 0;
|
||
com->uscsi_buflen = 0;
|
||
com->uscsi_flags = DK_DIAGNOSE|DK_SILENT;
|
||
|
||
rtn = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
/*
|
||
** This routine plays audio by msf
|
||
*/
|
||
static int
|
||
sr_play_msf(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
int rtn;
|
||
register struct cdrom_msf *msf;
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
msf = (struct cdrom_msf *)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_PLAYAUDIO_MSF;
|
||
cdb[3] = msf->cdmsf_min0;
|
||
cdb[4] = msf->cdmsf_sec0;
|
||
cdb[5] = msf->cdmsf_frame0;
|
||
cdb[6] = msf->cdmsf_min1;
|
||
cdb[7] = msf->cdmsf_sec1;
|
||
cdb[8] = msf->cdmsf_frame1;
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = 0;
|
||
com->uscsi_buflen = 0;
|
||
com->uscsi_flags = DK_DIAGNOSE|DK_SILENT;
|
||
|
||
rtn = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
/*
|
||
** This routine plays audio by track/index
|
||
*/
|
||
static int
|
||
sr_play_trkind(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
register struct cdrom_ti *ti;
|
||
int err;
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
|
||
ti = (struct cdrom_ti *)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_PLAYAUDIO_TI;
|
||
cdb[4] = ti->cdti_trk0;
|
||
cdb[5] = ti->cdti_ind0;
|
||
cdb[7] = ti->cdti_trk1;
|
||
cdb[8] = ti->cdti_ind1;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = 0;
|
||
com->uscsi_buflen = 0;
|
||
com->uscsi_flags = DK_DIAGNOSE|DK_SILENT;
|
||
|
||
err = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (err);
|
||
}
|
||
|
||
/*
|
||
** This routine starts the drive, stops the drive or ejects the disc
|
||
*/
|
||
static int
|
||
sr_start_stop(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
int rtn;
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(6)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
|
||
bzero(cdb, 6);
|
||
cdb[0] = SR_START_STOP;
|
||
cdb[1] = 0; /* immediate bit is set to 0 for now */
|
||
cdb[4] = (u_char)data;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 6;
|
||
com->uscsi_bufaddr = 0;
|
||
com->uscsi_buflen = 0;
|
||
com->uscsi_flags = DK_DIAGNOSE|DK_SILENT;
|
||
|
||
rtn = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 6);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
/*
|
||
** This routine ejects the CDROM disc
|
||
*/
|
||
/*ARGSUSED*/
|
||
static int
|
||
sr_eject(dev)
|
||
dev_t dev;
|
||
{
|
||
int err;
|
||
|
||
/* first, unlocks the eject */
|
||
if ((err = sr_medium_removal(dev, SR_REMOVAL_ALLOW)) != 0) {
|
||
return (err);
|
||
}
|
||
|
||
if ((err = srcmd(dev, SR_MEDIA_REMOV, 0, SR_MEDIA_unlock,
|
||
(caddr_t)0, 0, (caddr_t)0)) != 0) {
|
||
return (err);
|
||
}
|
||
|
||
/* then ejects the disc */
|
||
/* if ((err = (sr_start_stop(dev, (caddr_t)2))) == 0) {
|
||
sr_ejected(dev);
|
||
}
|
||
*/
|
||
err = srcmd(dev, SR_START_STOP, 0, SR_STOP_eject, (caddr_t)0, 0,
|
||
(caddr_t)0);
|
||
return (err);
|
||
}
|
||
|
||
/*
|
||
** This routine control the audio output volume
|
||
*/
|
||
#ifdef NOTSKIP
|
||
#define VolBufLng 20
|
||
static int
|
||
sr_volume_ctrl(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd cblk, *com = &cblk;
|
||
char cdb[10];
|
||
struct cdrom_volctrl *vol;
|
||
caddr_t buffer;
|
||
int rtn;
|
||
caddr_t tmp_ptr;
|
||
|
||
DPRINTF("in sr_volume_ctrl\n");
|
||
|
||
if ((buffer = SRIOPBALLOC(VolBufLng+4)) == (caddr_t)0) {
|
||
return (ENOMEM);
|
||
}
|
||
tmp_ptr = buffer;
|
||
buffer = (caddr_t)roundup((u_int)buffer, sizeof (int));
|
||
|
||
vol = (struct cdrom_volctrl *)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_CD_PLAYBACK_CONTROL; /* 0xc9 vendor unique command */
|
||
cdb[7] = 0;
|
||
cdb[8] = 18; /* length of control buffer passed */
|
||
|
||
/*
|
||
** fill in the input data. Set the output channel 0, 1 to
|
||
** output port 0, 1 respectively. Set output channel 2, 3 to mute
|
||
** The function only adjusts the output volume for channels 0 and 1.
|
||
*/
|
||
bzero(buffer, VolBufLng);
|
||
buffer[10] = 0x01;
|
||
buffer[11] = vol->channel0;
|
||
buffer[12] = 0x02;
|
||
buffer[13] = vol->channel1;
|
||
DPRINTF2("sr-volume ch0: 0x%x ch1:0x%x\n", vol->channel0,
|
||
vol->channel1);
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = buffer;
|
||
com->uscsi_buflen = 18;
|
||
com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_WRITE;
|
||
|
||
rtn = (srioctl_cmd(dev, (caddr_t)com, 0));
|
||
SRIOPBFREE(tmp_ptr, VolBufLng+4);
|
||
|
||
return (rtn);
|
||
}
|
||
#endif NOTSKIP
|
||
/*
|
||
** This routine control the audio output volume
|
||
*/
|
||
static int
|
||
sr_volume_ctrl(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
struct cdrom_volctrl *vol;
|
||
caddr_t buffer;
|
||
caddr_t tmp_ptr;
|
||
int rtn;
|
||
|
||
DPRINTF("in sr_volume_ctrl\n");
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(6)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
if ((buffer = SRIOPBALLOC(20)) == (caddr_t)0) {
|
||
return (ENOMEM);
|
||
}
|
||
tmp_ptr = buffer;
|
||
buffer = (caddr_t)roundup((u_int)buffer, sizeof (int));
|
||
|
||
vol = (struct cdrom_volctrl *)data;
|
||
bzero(cdb, 6);
|
||
cdb[0] = SCMD_MODE_SELECT;
|
||
cdb[4] = 20;
|
||
|
||
/*
|
||
* fill in the input data. Set the output channel 0, 1 to
|
||
* output port 0, 1 respestively. Set output channel 2, 3 to
|
||
* mute. The function only adjust the output volume for channel
|
||
* 0 and 1.
|
||
*/
|
||
bzero(buffer, 20);
|
||
buffer[4] = 0xe;
|
||
buffer[5] = 0xe;
|
||
buffer[6] = 0x4; /* set the immediate bit to 1 */
|
||
buffer[12] = 0x01;
|
||
buffer[13] = vol->channel0;
|
||
buffer[14] = 0x02;
|
||
buffer[15] = vol->channel1;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 6;
|
||
com->uscsi_bufaddr = buffer;
|
||
com->uscsi_buflen = 20;
|
||
com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_WRITE;
|
||
|
||
rtn = (srioctl_cmd(dev, (caddr_t)com, 0));
|
||
SRIOPBFREE(tmp_ptr, 20);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 6);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
static int
|
||
sr_read_subchannel(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
caddr_t buffer;
|
||
int rtn;
|
||
struct cdrom_subchnl *subchnl;
|
||
caddr_t tmp_ptr;
|
||
|
||
DPRINTF("in sr_read_subchannel\n");
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
if ((buffer = SRIOPBALLOC(16)) == (caddr_t)0) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
|
||
tmp_ptr = buffer;
|
||
buffer = (caddr_t)roundup((u_int)buffer, sizeof (int));
|
||
|
||
subchnl = (struct cdrom_subchnl *)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_READ_SUBCHANNEL;
|
||
cdb[1] = (subchnl->cdsc_format & CDROM_LBA) ? 0 : 0x02;
|
||
/*
|
||
* set the Q bit in byte 2 to 1.
|
||
*/
|
||
cdb[2] = 0x40;
|
||
|
||
/*
|
||
* This byte (byte 3) specifies the return data format. Proposed
|
||
* by Sony. To be added to SCSI-2 Rev 10b
|
||
* Setting it to one tells it to return time-data format
|
||
*/
|
||
cdb[3] = 0x01;
|
||
|
||
cdb[8] = 0x10;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = buffer;
|
||
com->uscsi_buflen = 0x10;
|
||
com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_READ;
|
||
|
||
rtn = (srioctl_cmd(dev, (caddr_t)com, 0));
|
||
subchnl->cdsc_audiostatus = buffer[1];
|
||
|
||
subchnl->cdsc_trk = buffer[6];
|
||
subchnl->cdsc_ind = buffer[7];
|
||
subchnl->cdsc_adr = buffer[5] & 0xF0;
|
||
subchnl->cdsc_ctrl = buffer[5] & 0x0F;
|
||
if (subchnl->cdsc_format & CDROM_LBA) {
|
||
subchnl->cdsc_absaddr.lba = ((u_char)buffer[8] << 24) +
|
||
((u_char)buffer[9] << 16) +
|
||
((u_char)buffer[10] << 8) +
|
||
((u_char)buffer[11]);
|
||
subchnl->cdsc_reladdr.lba = ((u_char)buffer[12] << 24) +
|
||
((u_char)buffer[13] << 16) +
|
||
((u_char)buffer[14] << 8) +
|
||
((u_char)buffer[15]);
|
||
} else {
|
||
subchnl->cdsc_absaddr.msf.minute = buffer[9];
|
||
subchnl->cdsc_absaddr.msf.second = buffer[10];
|
||
subchnl->cdsc_absaddr.msf.frame = buffer[11];
|
||
subchnl->cdsc_reladdr.msf.minute = buffer[13];
|
||
subchnl->cdsc_reladdr.msf.second = buffer[14];
|
||
subchnl->cdsc_reladdr.msf.frame = buffer[15];
|
||
}
|
||
SRIOPBFREE(tmp_ptr, 16);
|
||
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
static int
|
||
sr_read_tochdr(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
int rtn;
|
||
caddr_t buffer;
|
||
struct cdrom_tochdr *hdr;
|
||
caddr_t tmp_ptr;
|
||
|
||
DPRINTF("in sr_read_tochdr.\n");
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
if ((buffer = SRIOPBALLOC(4)) == (caddr_t)0) {
|
||
return (ENOMEM);
|
||
}
|
||
tmp_ptr = buffer;
|
||
buffer = (caddr_t)roundup((u_int)buffer, sizeof (int));
|
||
|
||
hdr = (struct cdrom_tochdr *)data;
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_READ_TOC;
|
||
/*
|
||
* for now, byte 6 is set to 1, assuming all cdrom's track
|
||
* starts with one. When the Sony/Hitachi drive conforms to
|
||
* SCSI specification, this byte will set to 0.
|
||
*/
|
||
/* cdb[6] = 0x01; */
|
||
/*
|
||
* byte 7, 8 are the allocation length. In this case, it is 4 bytes
|
||
*/
|
||
cdb[8] = 0x04;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
|
||
com->uscsi_bufaddr = buffer;
|
||
com->uscsi_buflen = 0x04;
|
||
com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_READ;
|
||
|
||
rtn = srioctl_cmd(dev, (caddr_t)com, 0);
|
||
|
||
hdr->cdth_trk0 = buffer[2];
|
||
hdr->cdth_trk1 = buffer[3];
|
||
|
||
SRIOPBFREE(tmp_ptr, 4);
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
/*
|
||
** This routine reads the toc of the disc and returns the information
|
||
** of a particular track. The track number is specified by the ioctl
|
||
** caller.
|
||
*/
|
||
static int
|
||
sr_read_tocentry(dev, data)
|
||
dev_t dev;
|
||
caddr_t data;
|
||
{
|
||
struct uscsi_cmd *com;
|
||
char *cdb;
|
||
struct cdrom_tocentry *entry;
|
||
caddr_t buffer, tmp_ptr;
|
||
int lba;
|
||
int rtn, rtn1;
|
||
|
||
DPRINTF("in sr_read_tocentry.\n");
|
||
|
||
/*
|
||
* So as not to worry about being swapped out, we dynamically
|
||
* allocate memory for the uscsi_cmd structure
|
||
*/
|
||
|
||
if ((com = (struct uscsi_cmd *)kmem_alloc(sizeof (struct uscsi_cmd)))
|
||
== (struct uscsi_cmd *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
bzero((char *)com, sizeof (struct uscsi_cmd));
|
||
|
||
if ((cdb = (char *)kmem_alloc(10)) == (char *)NULL) {
|
||
return (ENOMEM);
|
||
}
|
||
|
||
if ((buffer = SRIOPBALLOC(12)) == (caddr_t)0) {
|
||
return (ENOMEM);
|
||
}
|
||
tmp_ptr = buffer;
|
||
buffer = (caddr_t)roundup((u_int)buffer, sizeof (int));
|
||
|
||
entry = (struct cdrom_tocentry *)data;
|
||
if (!(entry->cdte_format & (CDROM_LBA | CDROM_MSF))) {
|
||
return (EINVAL);
|
||
}
|
||
bzero(cdb, 10);
|
||
|
||
cdb[0] = SCMD_READ_TOC;
|
||
/* set the MSF bit of byte one */
|
||
cdb[1] = (entry->cdte_format & CDROM_LBA) ? 0 : 2;
|
||
cdb[6] = entry->cdte_track;
|
||
/*
|
||
* byte 7, 8 are the allocation length. In this case, it is 4 + 8
|
||
* = 12 bytes, since we only need one entry.
|
||
*/
|
||
cdb[8] = 0x0C;
|
||
|
||
com->uscsi_cdb = cdb;
|
||
com->uscsi_cdblen = 10;
|
||
com->uscsi_bufaddr = buffer;
|
||
com->uscsi_buflen = 0xC;
|
||
com->uscsi_flags = USCSI_DIAGNOSE|USCSI_SILENT|USCSI_READ;
|
||
|
||
rtn = (srioctl_cmd(dev, (caddr_t)com, 0));
|
||
entry->cdte_adr = (buffer[5] & 0xF0) >> 4;
|
||
entry->cdte_ctrl = (buffer[5] & 0x0F);
|
||
if (entry->cdte_format & CDROM_LBA) {
|
||
entry->cdte_addr.lba = ((u_char)buffer[8] << 24) +
|
||
((u_char)buffer[9] << 16) +
|
||
((u_char)buffer[10] << 8) +
|
||
((u_char)buffer[11]);
|
||
} else {
|
||
entry->cdte_addr.msf.minute = buffer[9];
|
||
entry->cdte_addr.msf.second = buffer[10];
|
||
entry->cdte_addr.msf.frame = buffer[11];
|
||
}
|
||
if (rtn) {
|
||
SRIOPBFREE(tmp_ptr, 12);
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
/*
|
||
* Now do a readheader to determine which data mode it is in.
|
||
* ...If the track is a data track
|
||
*/
|
||
if ((entry->cdte_ctrl & CDROM_DATA_TRACK) &&
|
||
(entry->cdte_track != CDROM_LEADOUT)) {
|
||
if (entry->cdte_format & CDROM_LBA) {
|
||
lba = entry->cdte_addr.lba;
|
||
} else {
|
||
lba = (((entry->cdte_addr.msf.minute * 60) +
|
||
(entry->cdte_addr.msf.second)) * 75) +
|
||
entry->cdte_addr.msf.frame;
|
||
}
|
||
bzero(cdb, 10);
|
||
cdb[0] = SCMD_READ_HEADER;
|
||
cdb[2] = (u_char)((lba >> 24) & 0xFF);
|
||
cdb[3] = (u_char)((lba >> 16) & 0xFF);
|
||
cdb[4] = (u_char)((lba >> 8) & 0xFF);
|
||
cdb[5] = (u_char)(lba & 0xFF);
|
||
cdb[7] = 0x00;
|
||
cdb[8] = 0x08;
|
||
|
||
com->uscsi_buflen = 0x08;
|
||
|
||
rtn1 = (srioctl_cmd(dev, (caddr_t)com, 0));
|
||
if (rtn1) {
|
||
SRIOPBFREE(tmp_ptr, 12);
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
return (rtn1);
|
||
}
|
||
entry->cdte_datamode = buffer[0];
|
||
|
||
} else {
|
||
entry->cdte_datamode = -1;
|
||
}
|
||
|
||
SRIOPBFREE(tmp_ptr, 12);
|
||
kmem_free((caddr_t)com, sizeof (struct uscsi_cmd));
|
||
kmem_free((caddr_t)cdb, 10);
|
||
|
||
return (rtn);
|
||
}
|
||
|
||
static void
|
||
sr_setup_openflag(flag, dev_num)
|
||
struct cdrom_open_flag *flag;
|
||
u_char dev_num;
|
||
{
|
||
if ((flag->cdopen_flag0 == 0) &&
|
||
(flag->cdopen_flag1 == 0)) {
|
||
flag->cdopen_flag0 = dev_num;
|
||
} else if ((flag->cdopen_flag0 != dev_num) &&
|
||
(flag->cdopen_flag1 != dev_num)) {
|
||
if (flag->cdopen_flag0 == 0) {
|
||
flag->cdopen_flag0 = dev_num;
|
||
} else if (flag->cdopen_flag1 == 0) {
|
||
flag->cdopen_flag1 = dev_num;
|
||
} else {
|
||
printf("sropen: sr_open_flag corrupted\n");
|
||
}
|
||
}
|
||
}
|
||
|
||
static u_char sr_adaptec_keys[] = {
|
||
0, 4, 4, 4, 2, 4, 4, 4, /* 0x00-0x07 */
|
||
4, 4, 4, 4, 4, 4, 4, 4, /* 0x08-0x0f */
|
||
3, 3, 3, 3, 3, 3, 3, 1, /* 0x10-0x17 */
|
||
1, 1, 5, 5, 1, 1, 1, 1, /* 0x18-0x1f */
|
||
5, 5, 5, 5, 5, 5, 5, 5, /* 0x20-0x27 */
|
||
6, 6, 6, 6, 6, 6, 6, 6, /* 0x28-0x30 */
|
||
};
|
||
#define MAX_ADAPTEC_KEYS \
|
||
(sizeof (sr_adaptec_keys))
|
||
|
||
|
||
static char *sr_adaptec_error_str[] = {
|
||
"no sense", /* 0x00 */
|
||
"no index", /* 0x01 */
|
||
"no seek complete", /* 0x02 */
|
||
"write fault", /* 0x03 */
|
||
"drive not ready", /* 0x04 */
|
||
"drive not selected", /* 0x05 */
|
||
"no track 00", /* 0x06 */
|
||
"multiple drives selected", /* 0x07 */
|
||
"no address", /* 0x08 */
|
||
"no media loaded", /* 0x09 */
|
||
"end of media", /* 0x0a */
|
||
"", /* 0x0b */
|
||
"", /* 0x0c */
|
||
"", /* 0x0d */
|
||
"", /* 0x0e */
|
||
"", /* 0x0f */
|
||
"I.D. CRC error", /* 0x10 */
|
||
"hard data error", /* 0x11 */
|
||
"no I.D. address mark", /* 0x12 */
|
||
"no data address mark", /* 0x13 */
|
||
"record not found", /* 0x14 */
|
||
"seek error", /* 0x15 */
|
||
"DMA timeout error", /* 0x16 */
|
||
"write protected", /* 0x17 */
|
||
"correctable data error", /* 0x18 */
|
||
"bad block", /* 0x19 */
|
||
"interleave error", /* 0x1a */
|
||
"data transfer failed", /* 0x1b */
|
||
"bad format", /* 0x1c */
|
||
"self test failed", /* 0x1d */
|
||
"defective track", /* 0x1e */
|
||
0
|
||
};
|
||
#define MAX_ADAPTEC_ERROR_STR \
|
||
(sizeof (sr_adaptec_error_str)/sizeof (sr_adaptec_error_str[0]))
|
||
|
||
|
||
static char *sr_emulex_error_str[] = {
|
||
"no sense", /* 0x00 */
|
||
"recovered error", /* 0x01 */
|
||
"not ready", /* 0x02 */
|
||
"medium error", /* 0x03 */
|
||
"hardware error: not ready", /* 0x04 */
|
||
"illegal request", /* 0x05 */
|
||
"unit attention", /* 0x06 */
|
||
"multiple drives selected", /* 0x07 */
|
||
"lun failure", /* 0x08 */
|
||
"servo error", /* 0x09 */
|
||
"", /* 0x0a */
|
||
"aborted command", /* 0x0b */
|
||
"", /* 0x0c */
|
||
"", /* 0x0d */
|
||
"", /* 0x0e */
|
||
"", /* 0x0f */
|
||
"ID error", /* 0x10 */
|
||
"hard data error", /* 0x11 */
|
||
"no addr mark", /* 0x12 */
|
||
"no data field addr mark", /* 0x13 */
|
||
"block not found", /* 0x14 */
|
||
"seek error", /* 0x15 */
|
||
"dma timeout", /* 0x16 */
|
||
"recoverable error", /* 0x17 */
|
||
"soft data error", /* 0x18 */
|
||
"bad block", /* 0x19 */
|
||
"parameter overrun", /* 0x1a */
|
||
"synchronous xfer error", /* 0x1b */
|
||
"no primary defect list", /* 0x1c */
|
||
"compare error", /* 0x1d */
|
||
"recoverable error", /* 0x1e */
|
||
"", /* 0x1f */
|
||
"invalid command", /* 0x20 */
|
||
"invalid block", /* 0x21 */
|
||
"illegal command", /* 0x22 */
|
||
"", /* 0x23 */
|
||
"invalid cdb", /* 0x24 */
|
||
"invalid lun", /* 0x25 */
|
||
"invalid param list", /* 0x26 */
|
||
"write protected", /* 0x27 */
|
||
"media changed", /* 0x28 */
|
||
"reset", /* 0x29 */
|
||
"mode select changed", /* 0x2a */
|
||
0
|
||
};
|
||
#define MAX_EMULEX_ERROR_STR \
|
||
(sizeof (sr_emulex_error_str)/sizeof (sr_emulex_error_str[0]))
|
||
|
||
|
||
static char *sr_key_error_str[] = SENSE_KEY_INFO;
|
||
#define MAX_KEY_ERROR_STR \
|
||
(sizeof (sr_key_error_str)/sizeof (sr_key_error_str[0]))
|
||
|
||
|
||
static char *sr_cmds[] = {
|
||
"test unit ready", /* 0x00 */
|
||
"rezero", /* 0x01 */
|
||
"<bad cmd>", /* 0x02 */
|
||
"request sense", /* 0x03 */
|
||
"format", /* 0x04 */
|
||
"<bad cmd>", /* 0x05 */
|
||
"<bad cmd>", /* 0x06 */
|
||
"reassign", /* 0x07 */
|
||
"read", /* 0x08 */
|
||
"<bad cmd>", /* 0x09 */
|
||
"write", /* 0x0a */
|
||
"seek", /* 0x0b */
|
||
"<bad cmd>", /* 0x0c */
|
||
"<bad cmd>", /* 0x0d */
|
||
"<bad cmd>", /* 0x0e */
|
||
"<bad cmd>", /* 0x0f */
|
||
"<bad cmd>", /* 0x10 */
|
||
"<bad cmd>", /* 0x11 */
|
||
"inquiry", /* 0x12 */
|
||
"<bad cmd>", /* 0x13 */
|
||
"<bad cmd>", /* 0x14 */
|
||
"mode select", /* 0x15 */
|
||
"reserve", /* 0x16 */
|
||
"release", /* 0x17 */
|
||
"copy", /* 0x18 */
|
||
"<bad cmd>", /* 0x19 */
|
||
"mode sense", /* 0x1a */
|
||
"start/stop unit(LoEj)", /* 0x1b */
|
||
"<bad cmd>", /* 0x1c */
|
||
"send diagnostic", /* 0x1d */
|
||
"prevent-allow removal", /* 0x1e */
|
||
"<bad cmd>", /* 0x1f */
|
||
};
|
||
#define MAX_SR_CMDS \
|
||
(sizeof (sr_cmds)/sizeof (sr_cmds[0]))
|
||
|
||
|
||
/*
|
||
** Translate Adaptec non-extended sense status in to
|
||
** extended sense format. In other words, generate
|
||
** sense key.
|
||
*/
|
||
static
|
||
srintr_adaptec(dsi)
|
||
register struct scsi_disk *dsi;
|
||
{
|
||
register struct scsi_sense *s;
|
||
register struct scsi_ext_sense *ssr;
|
||
|
||
EPRINTF("srintr_adaptec\n");
|
||
|
||
/* Reposition failed block number for extended sense. */
|
||
(u_int)ssr = (u_int)s = dsi->un_sense;
|
||
ssr->info_1 = 0;
|
||
ssr->info_2 = s->high_addr;
|
||
ssr->info_3 = s->mid_addr;
|
||
ssr->info_4 = s->low_addr;
|
||
|
||
/* Reposition error code for extended sense. */
|
||
ssr->error_code = s->code;
|
||
|
||
/* Synthesize sense key for extended sense. */
|
||
if (s->code < MAX_ADAPTEC_KEYS) {
|
||
ssr->key = sr_adaptec_keys[s->code];
|
||
}
|
||
}
|
||
|
||
|
||
/*
|
||
** Return the text string associated with the sense key value.
|
||
*/
|
||
static char *
|
||
sr_print_key(key_code)
|
||
register u_char key_code;
|
||
{
|
||
static char *unknown_key = "unknown key";
|
||
if ((key_code > MAX_KEY_ERROR_STR -1) ||
|
||
sr_key_error_str[key_code] == NULL) {
|
||
|
||
return (unknown_key);
|
||
}
|
||
return (sr_key_error_str[key_code]);
|
||
}
|
||
|
||
|
||
/*
|
||
** Return the text string associated with the secondary
|
||
** error code, if availiable.
|
||
*/
|
||
static char *
|
||
sr_print_error(dsi, error_code)
|
||
register struct scsi_disk *dsi;
|
||
register u_char error_code;
|
||
{
|
||
static char *unknown_error = "unknown error";
|
||
switch (dsi->un_ctype) {
|
||
case CTYPE_TOSHIBA:
|
||
case CTYPE_MD21:
|
||
case CTYPE_CCS:
|
||
if ((MAX_EMULEX_ERROR_STR > error_code) &&
|
||
sr_emulex_error_str[error_code] != NULL)
|
||
return (sr_emulex_error_str[error_code]);
|
||
break;
|
||
|
||
case CTYPE_ACB4000:
|
||
if (MAX_ADAPTEC_ERROR_STR > error_code &&
|
||
sr_adaptec_error_str[error_code] != NULL)
|
||
return (sr_adaptec_error_str[error_code]);
|
||
break;
|
||
}
|
||
return (unknown_error);
|
||
}
|
||
|
||
|
||
/*
|
||
** Print the sense key and secondary error codes
|
||
** and dump out the sense bytes.
|
||
*/
|
||
#ifdef SRDEBUG
|
||
static
|
||
sr_error_message(un, dsi)
|
||
register struct scsi_unit *un;
|
||
register struct scsi_disk *dsi;
|
||
{
|
||
register struct scsi_ext_sense *ssr;
|
||
register u_char *cp;
|
||
register int i;
|
||
register u_char error_code;
|
||
|
||
/* If error messages are being suppressed, exit. */
|
||
if (dsi->un_options & SD_SILENT)
|
||
return;
|
||
|
||
ssr = (struct scsi_ext_sense *)cp = (
|
||
struct scsi_ext_sense *)dsi->un_sense;
|
||
error_code = ssr->error_code;
|
||
printf("sr%d error: sense key(0x%x): %s",
|
||
un - srunits, ssr->key, sr_print_key(ssr->key));
|
||
if (error_code != 0) {
|
||
printf(", error code(0x%x): %s",
|
||
error_code, sr_print_error(dsi, error_code));
|
||
}
|
||
|
||
printf("\n sense = ");
|
||
for (i = 0; i < sizeof (struct scsi_ext_sense); i++)
|
||
printf("%x ", *cp++);
|
||
printf("\n");
|
||
}
|
||
|
||
|
||
static
|
||
sr_print_cmd(un)
|
||
register struct scsi_unit *un;
|
||
{
|
||
register int x;
|
||
register u_char *y = (u_char *)&(un->un_cdb);
|
||
|
||
printf("sr%d: failed cmd = ", SRUNIT(un->un_unit));
|
||
for (x = 0; x < CDB_GROUP0; x++)
|
||
printf("%x ", *y++);
|
||
printf("\n");
|
||
}
|
||
#endif SRDEBUG
|
||
|
||
sr_makecom_all(cdb, scsi_cdb)
|
||
char *cdb;
|
||
struct scsi_cdb *scsi_cdb;
|
||
{
|
||
int group;
|
||
|
||
group = ((unsigned char)cdb_cmd(cdb)) >> 5;
|
||
scsi_cdb->scc_cmd = cdb_cmd(cdb);
|
||
scsi_cdb->scc_lun = 0;
|
||
switch (group) {
|
||
case 0:
|
||
scsi_cdb->g0_addr2 = cdb_tag(cdb);
|
||
scsi_cdb->g0_addr1 = (u_char)cdb[2];
|
||
scsi_cdb->g0_addr0 = (u_char)cdb[3];
|
||
scsi_cdb->g0_count0 = (u_char)cdb[4];
|
||
scsi_cdb->g0_vu_1 = cdb[5] & 0x80;
|
||
scsi_cdb->g0_vu_0 = cdb[5] & 0x40;
|
||
break;
|
||
case 1:
|
||
case 2:
|
||
case 6: /* edu: only for now. eventually, we will not */
|
||
/* support group 6. */
|
||
scsi_cdb->g1_reladdr = cdb_tag(cdb);
|
||
scsi_cdb->g1_addr3 = (u_char)cdb[2];
|
||
scsi_cdb->g1_addr2 = (u_char)cdb[3];
|
||
scsi_cdb->g1_addr1 = (u_char)cdb[4];
|
||
scsi_cdb->g1_addr0 = (u_char)cdb[5];
|
||
scsi_cdb->g1_rsvd0 = (u_char)cdb[6];
|
||
scsi_cdb->g1_count1 = (u_char)cdb[7];
|
||
scsi_cdb->g1_count0 = (u_char)cdb[8];
|
||
scsi_cdb->g1_vu_1 = cdb[9] & 0x80;
|
||
scsi_cdb->g1_vu_0 = cdb[9] & 0x40;
|
||
break;
|
||
default:
|
||
DPRINTF1("makecom_all: does not support group %d\n",
|
||
group);
|
||
}
|
||
}
|
||
|
||
static
|
||
srerrmsg(un, bp, action)
|
||
struct scsi_unit *un;
|
||
struct buf *bp;
|
||
char *action;
|
||
{
|
||
register struct scsi_ext_sense *ssr;
|
||
register struct scsi_disk *dsi;
|
||
char *cmdname;
|
||
u_char error_code;
|
||
int blkno_flag = 1; /* if false, blkno is meaningless */
|
||
|
||
dsi = &srdisk[dkunit(bp)];
|
||
ssr = (struct scsi_ext_sense *)dsi->un_sense;
|
||
|
||
/* If error messages are being suppressed, exit. */
|
||
if (dsi->un_options & SD_SILENT)
|
||
return;
|
||
|
||
/* Decode command name */
|
||
if (un->un_cmd < MAX_SR_CMDS) {
|
||
cmdname = sr_cmds[un->un_cmd];
|
||
} else {
|
||
cmdname = "unknown cmd";
|
||
}
|
||
|
||
/* If not a check condition error, block number is invalid */
|
||
if (! un->un_scb.chk)
|
||
blkno_flag = 0;
|
||
|
||
|
||
printf("sr%d%c: %s %s",
|
||
SRNUM(un), LPART(bp->b_dev) + 'a', cmdname, action);
|
||
if (blkno_flag)
|
||
printf(", block %d", dsi->un_err_blkno);
|
||
|
||
error_code = ssr->error_code;
|
||
printf("\n sense key(0x%x): %s",
|
||
ssr->key, sr_print_key(ssr->key));
|
||
if (error_code != 0) {
|
||
printf(", error code(0x%x): %s\n",
|
||
error_code, sr_print_error(dsi, error_code));
|
||
} else {
|
||
printf("\n");
|
||
}
|
||
|
||
}
|
||
#endif NSR > 0
|