* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Van Jacobson of Lawrence Berkeley Laboratory.
* %sccs.include.redist.c%
* @(#)sd.c 7.14 (Berkeley) %G%
* SCSI CCS (Command Command Set) disk driver.
static char rcsid
[] = "$Header: /usr/src/sys/hp300/dev/RCS/sd.c,v 1.2 92/04/10 20:48:35 mike Exp $";
#include "sys/disklabel.h"
#include "hp/dev/device.h"
extern int scsi_test_unit_rdy();
extern int scsi_request_sense();
extern int scsi_inquiry();
extern int scsi_read_capacity();
extern int scsi_tt_write();
extern void scsi_delay();
void sdstrategy(), sdstart(), sdustart(), sdgo(), sdintr();
struct driver sddriver
= {
sdinit
, "sd", (int (*)())sdstart
, (int (*)())sdgo
, (int (*)())sdintr
,
* since the SCSI standard tends to hide the disk structure, we define
* partitions in terms of DEV_BSIZE blocks. The default partition table
* (for an unlabeled disk) reserves 512K for a boot area, has an 8 meg
* root and 32 meg of swap. The rest of the space on the drive goes in
* the G partition. As usual, the C partition covers the entire disk
* (including the boot area).
struct sdinfo sddefaultpart
= {
1024, 17408, 16384 , /* A */
17408, 82944, 65536 , /* B */
17408, 115712, 98304 , /* D */
115712, 218112, 102400 , /* E */
int sc_format_pid
; /* process using "format" mode */
short sc_type
; /* drive type */
short sc_punit
; /* physical unit (scsi lun) */
u_short sc_bshift
; /* convert device blocks to DEV_BSIZE blks */
u_int sc_blks
; /* number of blocks on device */
int sc_blksize
; /* device block size in bytes */
u_int sc_wpms
; /* average xfer rate in 16 bit wds/sec. */
struct sdinfo sc_info
; /* drive partition table & label info */
struct scsi_fmt_cdb sdcmd
[NSD
];
struct scsi_fmt_sense sdsense
[NSD
];
static struct scsi_fmt_cdb sd_read_cmd
= { 10, CMD_READ_EXT
};
static struct scsi_fmt_cdb sd_write_cmd
= { 10, CMD_WRITE_EXT
};
#define sdunit(x) (minor(x) >> 3)
#define sdpart(x) (minor(x) & 0x7)
#define sdpunit(x) ((x) & 7)
* Table of scsi commands users are allowed to access via "format"
* mode. 0 means not legal. 1 means "immediate" (doesn't need dma).
* -1 means needs dma and/or wait for intr.
static char legal_cmds
[256] = {
/***** 0 1 2 3 4 5 6 7 8 9 A B C D E F */
/*00*/ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*10*/ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
/*20*/ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*30*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*50*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*60*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*70*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*80*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*90*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*a0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*b0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*c0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*d0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*e0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/*f0*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
static struct scsi_inquiry inqbuf
;
static struct scsi_fmt_cdb inq
= {
CMD_INQUIRY
, 0, 0, 0, sizeof(inqbuf
), 0
struct scsi_fmt_cdb cap
= {
CMD_READ_CAPACITY
, 0, 0, 0, 0, 0, 0, 0, 0, 0
register int ctlr
, slave
;
* See if unit exists and is a disk then read block size & nblocks.
while ((i
= scsi_test_unit_rdy(ctlr
, slave
, unit
)) != 0) {
if (i
== -1 || --tries
< 0) {
/* doesn't exist or not a CCS device */
if (i
== STS_CHECKCOND
) {
struct scsi_xsense
*sp
= (struct scsi_xsense
*)sensebuf
;
scsi_request_sense(ctlr
, slave
, unit
, sensebuf
,
/* not ready -- might be MO with no media */
sensebuf
[12] == 10) /* XXX */
/* drive doing an RTZ -- give it a while */
if (scsi_immed_command(ctlr
, slave
, unit
, &inq
,
(u_char
*)&inqbuf
, sizeof(inqbuf
), B_READ
))
case 7: /* Magneto-optical */
default: /* not a disk */
if (inqbuf
.version
!= 1) {
bcopy("UNKNOWN", &idstr
[0], 8);
bcopy("DRIVE TYPE", &idstr
[8], 11);
bcopy((caddr_t
)&inqbuf
.vendor_id
, (caddr_t
)idstr
, 28);
for (i
= 27; i
> 23; --i
)
i
= scsi_immed_command(ctlr
, slave
, unit
, &cap
,
(u_char
*)&capbuf
, sizeof(capbuf
), B_READ
);
if (i
!= STS_CHECKCOND
||
bcmp(&idstr
[0], "HP", 3) ||
bcmp(&idstr
[8], "S6300.650A", 11))
/* XXX unformatted or non-existant MO media; fake it */
sc
->sc_blks
= *(u_int
*)&capbuf
[0];
sc
->sc_blksize
= *(int *)&capbuf
[4];
/* return value of read capacity is last valid block number */
printf("sd%d: type 0x%x, qual 0x%x, ver %d", hd
->hp_unit
,
inqbuf
.type
, inqbuf
.qual
, inqbuf
.version
);
printf("sd%d: %s %s rev %s", hd
->hp_unit
, idstr
, &idstr
[8],
printf(", %d %d byte blocks\n", sc
->sc_blks
, sc
->sc_blksize
);
sc
->sc_flags
|= SDF_RMEDIA
;
if (sc
->sc_blksize
!= DEV_BSIZE
) {
if (sc
->sc_blksize
< DEV_BSIZE
) {
printf("sd%d: need %d byte blocks - drive ignored\n",
for (i
= sc
->sc_blksize
; i
> DEV_BSIZE
; i
>>= 1)
sc
->sc_blks
<<= sc
->sc_bshift
;
sc
->sc_wpms
= 32 * (60 * DEV_BSIZE
/ 2); /* XXX */
register struct hp_device
*hd
;
register struct sd_softc
*sc
= &sd_softc
[hd
->hp_unit
];
sc
->sc_punit
= sdpunit(hd
->hp_flags
);
sc
->sc_type
= sdident(sc
, hd
);
sc
->sc_dq
.dq_ctlr
= hd
->hp_ctlr
;
sc
->sc_dq
.dq_unit
= hd
->hp_unit
;
sc
->sc_dq
.dq_slave
= hd
->hp_slave
;
sc
->sc_dq
.dq_driver
= &sddriver
;
* If we don't have a disk label, build a default partition
* table with 'standard' size root & swap and everything else
sc
->sc_info
= sddefaultpart
;
sc
->sc_info
.part
[2].nblocks
= sc
->sc_blks
;
sc
->sc_info
.part
[2].endblk
= sc
->sc_blks
;
/* G gets from end of B to end of disk */
sc
->sc_info
.part
[6].nblocks
= sc
->sc_blks
- sc
->sc_info
.part
[1].endblk
;
sc
->sc_info
.part
[6].endblk
= sc
->sc_blks
;
* We also define the D, E and F paritions as an alternative to
* B and G. D is 48Mb, starts after A and is intended for swapping.
* E is 50Mb, starts after D and is intended for /usr. F starts
* after E and is what ever is left.
if (sc
->sc_blks
>= sc
->sc_info
.part
[4].endblk
) {
sc
->sc_info
.part
[5].nblocks
=
sc
->sc_blks
- sc
->sc_info
.part
[4].endblk
;
sc
->sc_info
.part
[5].endblk
= sc
->sc_blks
;
sc
->sc_info
.part
[5].strtblk
= 0;
sc
->sc_info
.part
[3] = sc
->sc_info
.part
[5];
sc
->sc_info
.part
[4] = sc
->sc_info
.part
[5];
* H is a single partition alternative to E and F.
if (sc
->sc_blks
>= sc
->sc_info
.part
[3].endblk
) {
sc
->sc_info
.part
[7].nblocks
=
sc
->sc_blks
- sc
->sc_info
.part
[3].endblk
;
sc
->sc_info
.part
[7].endblk
= sc
->sc_blks
;
sc
->sc_info
.part
[7].strtblk
= 0;
sc
->sc_flags
|= SDF_ALIVE
;
register struct sd_softc
*sc
;
register struct hp_device
*hd
;
sdstats
[hd
->hp_unit
].sdresets
++;
sdopen(dev
, flags
, mode
, p
)
register int unit
= sdunit(dev
);
register struct sd_softc
*sc
= &sd_softc
[unit
];
if ((sc
->sc_flags
& SDF_ALIVE
) == 0 && suser(p
->p_ucred
, &p
->p_acflag
))
if (sc
->sc_hd
->hp_dk
>= 0)
dk_wpms
[sc
->sc_hd
->hp_dk
] = sc
->sc_wpms
;
sdclose(dev
, flag
, mode
, p
)
register struct sd_softc
*sc
= &sd_softc
[unit
];
* XXX we should really do this for all drives.
if (sc
->sc_flags
& SDF_RMEDIA
) {
while (sdtab
[unit
].b_active
) {
sc
->sc_flags
|= SDF_WANTED
;
sleep((caddr_t
)&sdtab
[unit
], PRIBIO
);
* This routine is called for partial block transfers and non-aligned
* transfers (the latter only being possible on devices with a block size
* larger than DEV_BSIZE). The operation is performed in three steps
* using a locally allocated buffer:
* 1. transfer any initial partial block
* 2. transfer full blocks
* 3. transfer any final partial block
register struct buf
*cbp
= (struct buf
*)malloc(sizeof(struct buf
),
caddr_t cbuf
= (caddr_t
)malloc(bsize
, M_DEVBUF
, M_WAITOK
);
bzero((caddr_t
)cbp
, sizeof(*cbp
));
cbp
->b_proc
= curproc
; /* XXX */
if (sddebug
& SDB_PARTIAL
)
printf("sdlblkstrat: bp %x flags %x bn %x resid %x addr %x\n",
bp
, bp
->b_flags
, bn
, resid
, addr
);
register int boff
= dbtob(bn
) & (bsize
- 1);
if (boff
|| resid
< bsize
) {
sdstats
[sdunit(bp
->b_dev
)].sdpartials
++;
count
= min(resid
, bsize
- boff
);
cbp
->b_flags
= B_BUSY
| B_PHYS
| B_READ
;
cbp
->b_blkno
= bn
- btodb(boff
);
if (sddebug
& SDB_PARTIAL
)
printf(" readahead: bn %x cnt %x off %x addr %x\n",
cbp
->b_blkno
, count
, boff
, addr
);
if (cbp
->b_flags
& B_ERROR
) {
bp
->b_error
= cbp
->b_error
;
if (bp
->b_flags
& B_READ
) {
bcopy(&cbuf
[boff
], addr
, count
);
bcopy(addr
, &cbuf
[boff
], count
);
if (sddebug
& SDB_PARTIAL
)
printf(" writeback: bn %x cnt %x off %x addr %x\n",
cbp
->b_blkno
, count
, boff
, addr
);
count
= resid
& ~(bsize
- 1);
if (sddebug
& SDB_PARTIAL
)
printf(" fulltrans: bn %x cnt %x addr %x\n",
cbp
->b_blkno
, count
, addr
);
cbp
->b_flags
= B_BUSY
| B_PHYS
| (bp
->b_flags
& B_READ
);
if (cbp
->b_flags
& B_ERROR
) {
bp
->b_error
= cbp
->b_error
;
if (sddebug
& SDB_PARTIAL
)
printf(" done: bn %x resid %x addr %x\n",
register int unit
= sdunit(bp
->b_dev
);
register struct sd_softc
*sc
= &sd_softc
[unit
];
register struct size
*pinfo
= &sc
->sc_info
.part
[sdpart(bp
->b_dev
)];
register struct buf
*dp
= &sdtab
[unit
];
if (sc
->sc_format_pid
!= curproc
->p_pid
) { /* XXX */
sz
= howmany(bp
->b_bcount
, DEV_BSIZE
);
if (bn
< 0 || bn
+ sz
> pinfo
->nblocks
) {
sz
= pinfo
->nblocks
- bn
;
bp
->b_resid
= bp
->b_bcount
;
bp
->b_bcount
= dbtob(sz
);
* Non-aligned or partial-block transfers handled specially.
if ((dbtob(bn
) & s
) || (bp
->b_bcount
& s
)) {
sdlblkstrat(bp
, sc
->sc_blksize
);
bp
->b_cylin
= (bn
+ pinfo
->strtblk
) >> sc
->sc_bshift
;
if (scsireq(&sd_softc
[unit
].sc_dq
))
* 0 if not really an error
* <0 if we should do a retry
sderror(unit
, sc
, hp
, stat
)
register struct sd_softc
*sc
;
register struct hp_device
*hp
;
sdsense
[unit
].status
= stat
;
if (stat
& STS_CHECKCOND
) {
scsi_request_sense(hp
->hp_ctlr
, hp
->hp_slave
,
sc
->sc_punit
, sdsense
[unit
].sense
,
sizeof(sdsense
[unit
].sense
));
sp
= (struct scsi_xsense
*)sdsense
[unit
].sense
;
printf("sd%d: scsi sense class %d, code %d", unit
,
printf(", key %d", sp
->key
);
printf(", blk %d", *(int *)&sp
->info1
);
/* no sense, try again */
/* recovered error, not a problem */
register struct sd_softc
*sc
;
register struct buf
*dp
= &sdtab
[unit
];
if (sc
->sc_flags
& SDF_WANTED
) {
sc
->sc_flags
&= ~SDF_WANTED
;
register struct sd_softc
*sc
= &sd_softc
[unit
];
register struct hp_device
*hp
= sc
->sc_hd
;
* we have the SCSI bus -- in format mode, we may or may not need dma
if (sc
->sc_format_pid
&& legal_cmds
[sdcmd
[unit
].cdb
[0]] > 0) {
register struct buf
*bp
= sdtab
[unit
].b_actf
;
sts
= scsi_immed_command(hp
->hp_ctlr
, hp
->hp_slave
,
sc
->sc_punit
, &sdcmd
[unit
],
bp
->b_un
.b_addr
, bp
->b_bcount
,
sdsense
[unit
].status
= sts
;
(void) sderror(unit
, sc
, hp
, sts
);
} else if (scsiustart(hp
->hp_ctlr
))
register struct sd_softc
*sc
= &sd_softc
[unit
];
register struct hp_device
*hp
= sc
->sc_hd
;
register struct buf
*bp
= sdtab
[unit
].b_actf
;
register struct scsi_fmt_cdb
*cmd
;
cmd
= bp
->b_flags
& B_READ
? &sd_read_cmd
: &sd_write_cmd
;
*(int *)(&cmd
->cdb
[2]) = bp
->b_cylin
;
pad
= howmany(bp
->b_bcount
, sc
->sc_blksize
);
*(u_short
*)(&cmd
->cdb
[7]) = pad
;
pad
= (bp
->b_bcount
& (sc
->sc_blksize
- 1)) != 0;
printf("sd%d: partial block xfer -- %x bytes\n",
sdstats
[unit
].sdtransfers
++;
if (scsigo(hp
->hp_ctlr
, hp
->hp_slave
, sc
->sc_punit
, bp
, cmd
, pad
) == 0) {
dk_busy
|= 1 << hp
->hp_dk
;
dk_wds
[hp
->hp_dk
] += bp
->b_bcount
>> 6;
printf("sd%d: sdstart: %s adr %d blk %d len %d ecnt %d\n",
unit
, bp
->b_flags
& B_READ
? "read" : "write",
bp
->b_un
.b_addr
, bp
->b_cylin
, bp
->b_bcount
,
register struct sd_softc
*sc
= &sd_softc
[unit
];
register struct buf
*bp
= sdtab
[unit
].b_actf
;
register struct hp_device
*hp
= sc
->sc_hd
;
printf("sd%d: bp == NULL\n", unit
);
dk_busy
&=~ (1 << hp
->hp_dk
);
printf("sd%d: sdintr: bad scsi status 0x%x\n",
cond
= sderror(unit
, sc
, hp
, stat
);
if (cond
< 0 && sdtab
[unit
].b_errcnt
++ < SDRETRY
) {
printf("sd%d: retry #%d\n",
unit
, sdtab
[unit
].b_errcnt
);
register int unit
= sdunit(dev
);
if ((pid
= sd_softc
[unit
].sc_format_pid
) &&
pid
!= uio
->uio_procp
->p_pid
)
return (physio(sdstrategy
, NULL
, dev
, B_READ
, minphys
, uio
));
register int unit
= sdunit(dev
);
if ((pid
= sd_softc
[unit
].sc_format_pid
) &&
pid
!= uio
->uio_procp
->p_pid
)
return (physio(sdstrategy
, NULL
, dev
, B_WRITE
, minphys
, uio
));
sdioctl(dev
, cmd
, data
, flag
, p
)
register int unit
= sdunit(dev
);
register struct sd_softc
*sc
= &sd_softc
[unit
];
/* take this device into or out of "format" mode */
if (suser(p
->p_ucred
, &p
->p_acflag
))
sc
->sc_format_pid
= p
->p_pid
;
/* find out who has the device in format mode */
*(int *)data
= sc
->sc_format_pid
;
* Save what user gave us as SCSI cdb to use with next
* read or write to the char device.
if (sc
->sc_format_pid
!= p
->p_pid
)
if (legal_cmds
[((struct scsi_fmt_cdb
*)data
)->cdb
[0]] == 0)
bcopy(data
, (caddr_t
)&sdcmd
[unit
], sizeof(sdcmd
[0]));
* return the SCSI sense data saved after the last
* operation that completed with "check condition" status.
bcopy((caddr_t
)&sdsense
[unit
], data
, sizeof(sdsense
[0]));
register int unit
= sdunit(dev
);
register struct sd_softc
*sc
= &sd_softc
[unit
];
if (unit
>= NSD
|| (sc
->sc_flags
& SDF_ALIVE
) == 0)
return(sc
->sc_info
.part
[sdpart(dev
)].nblocks
);
* Non-interrupt driven, non-dma dump routine.
register struct sd_softc
*sc
= &sd_softc
[unit
];
register struct hp_device
*hp
= sc
->sc_hd
;
* Hmm... all vax drivers dump maxfree pages which is physmem minus
* the message buffer. Is there a reason for not dumping the
* message buffer? Savecore expects to read 'dumpsize' pages of
* dump, where dumpsys() sets dumpsize to physmem!
if (unit
>= NSD
|| (sc
->sc_flags
& SDF_ALIVE
) == 0)
/* dump parameters in range? */
if (dumplo
< 0 || dumplo
>= sc
->sc_info
.part
[part
].nblocks
)
if (dumplo
+ ctod(pages
) > sc
->sc_info
.part
[part
].nblocks
)
pages
= dtoc(sc
->sc_info
.part
[part
].nblocks
- dumplo
);
baddr
= dumplo
+ sc
->sc_info
.part
[part
].strtblk
;
if (!scsireq(&sc
->sc_dq
)) {
printf("[ drive %d reset ] ", unit
);
for (i
= 0; i
< pages
; i
++) {
#define NPGMB (1024*1024/NBPG)
/* print out how many Mbs we have dumped */
if (i
&& (i
% NPGMB
) == 0)
printf("%d ", i
/ NPGMB
);
pmap_enter(kernel_pmap
, (vm_offset_t
)vmmap
, maddr
,
stat
= scsi_tt_write(hp
->hp_ctlr
, hp
->hp_slave
, sc
->sc_punit
,
vmmap
, NBPG
, baddr
, sc
->sc_bshift
);
printf("sddump: scsi write error 0x%x\n", stat
);