diff options
Diffstat (limited to 'sys/scsi')
-rw-r--r-- | sys/scsi/sd.c | 97 |
1 files changed, 77 insertions, 20 deletions
diff --git a/sys/scsi/sd.c b/sys/scsi/sd.c index 2d8ef52df38..474ae5d0281 100644 --- a/sys/scsi/sd.c +++ b/sys/scsi/sd.c @@ -1,4 +1,4 @@ -/* $NetBSD: sd.c,v 1.80 1995/10/10 02:53:01 mycroft Exp $ */ +/* $NetBSD: sd.c,v 1.83 1995/12/07 21:54:24 thorpej Exp $ */ /* * Copyright (c) 1994, 1995 Charles M. Hannum. All rights reserved. @@ -84,6 +84,7 @@ struct sd_softc { #define SDF_WANTED 0x02 #define SDF_WLABEL 0x04 /* label is writable */ #define SDF_LABELLING 0x08 /* writing label */ +#define SDF_ANCIENT 0x10 /* disk is ancient; for minphys */ struct scsi_link *sc_link; /* contains our targ, lun, etc. */ struct disk_parms { u_char heads; /* number of heads */ @@ -106,6 +107,7 @@ void sdgetdisklabel __P((struct sd_softc *)); int sd_get_parms __P((struct sd_softc *, int)); void sdstrategy __P((struct buf *)); void sdstart __P((struct sd_softc *)); +void sdminphys __P((struct buf *)); struct dkdriver sddkdriver = { sdstrategy }; @@ -167,6 +169,12 @@ sdattach(parent, self, aux) if (sc_link->openings > SDOUTSTANDING) sc_link->openings = SDOUTSTANDING; + /* + * Note if this device is ancient. This is used in sdminphys(). + */ + if ((sa->sa_inqbuf->version & SID_ANSII) == 0) + sd->flags |= SDF_ANCIENT; + sd->sc_dk.dk_driver = &sddkdriver; #if !defined(i386) || defined(NEWCONFIG) dk_establish(&sd->sc_dk, &sd->sc_dev); @@ -466,8 +474,10 @@ sdstart(sd) register struct scsi_link *sc_link = sd->sc_link; struct buf *bp = 0; struct buf *dp; - struct scsi_rw_big cmd; - int blkno, nblks; + struct scsi_rw_big cmd_big; + struct scsi_rw cmd_small; + struct scsi_generic *cmdp; + int blkno, nblks, cmdlen; struct partition *p; SC_DEBUG(sc_link, SDEV_DB2, ("sdstart ")); @@ -521,38 +531,87 @@ sdstart(sd) nblks = howmany(bp->b_bcount, sd->sc_dk.dk_label.d_secsize); /* - * Fill out the scsi command + * Fill out the scsi command. If the transfer will + * fit in a "small" cdb, use it. */ - bzero(&cmd, sizeof(cmd)); - cmd.opcode = (bp->b_flags & B_READ) ? READ_BIG : WRITE_BIG; - cmd.addr_3 = (blkno >> 24) & 0xff; - cmd.addr_2 = (blkno >> 16) & 0xff; - cmd.addr_1 = (blkno >> 8) & 0xff; - cmd.addr_0 = blkno & 0xff; - cmd.length2 = (nblks >> 8) & 0xff; - cmd.length1 = nblks & 0xff; + if (((blkno & 0x1fffff) == blkno) && + ((nblks & 0xff) == nblks)) { + /* + * We can fit in a small cdb. + */ + bzero(&cmd_small, sizeof(cmd_small)); + cmd_small.opcode = (bp->b_flags & B_READ) ? + READ_COMMAND : WRITE_COMMAND; + cmd_small.addr_2 = (blkno >> 16) & 0x1f; + cmd_small.addr_1 = (blkno >> 8) & 0xff; + cmd_small.addr_0 = blkno & 0xff; + cmd_small.length = nblks & 0xff; + cmdlen = sizeof(cmd_small); + cmdp = (struct scsi_generic *)&cmd_small; + } else { + /* + * Need a large cdb. + */ + bzero(&cmd_big, sizeof(cmd_big)); + cmd_big.opcode = (bp->b_flags & B_READ) ? + READ_BIG : WRITE_BIG; + cmd_big.addr_3 = (blkno >> 24) & 0xff; + cmd_big.addr_2 = (blkno >> 16) & 0xff; + cmd_big.addr_1 = (blkno >> 8) & 0xff; + cmd_big.addr_0 = blkno & 0xff; + cmd_big.length2 = (nblks >> 8) & 0xff; + cmd_big.length1 = nblks & 0xff; + cmdlen = sizeof(cmd_big); + cmdp = (struct scsi_generic *)&cmd_big; + } /* * Call the routine that chats with the adapter. * Note: we cannot sleep as we may be an interrupt */ - if (scsi_scsi_cmd(sc_link, (struct scsi_generic *)&cmd, - sizeof(cmd), (u_char *)bp->b_data, bp->b_bcount, + if (scsi_scsi_cmd(sc_link, cmdp, cmdlen, + (u_char *)bp->b_data, bp->b_bcount, SDRETRIES, 10000, bp, SCSI_NOSLEEP | ((bp->b_flags & B_READ) ? SCSI_DATA_IN : SCSI_DATA_OUT))) printf("%s: not queued", sd->sc_dev.dv_xname); } } +void +sdminphys(bp) + struct buf *bp; +{ + struct sd_softc *sd = sdcd.cd_devs[SDUNIT(bp->b_dev)]; + long max; + + /* + * If the device is ancient, we want to make sure that + * the transfer fits into a 6-byte cdb. + * + * XXX Note that the SCSI-I spec says that 256-block transfers + * are allowed in a 6-byte read/write, and are specified + * by settng the "length" to 0. However, we're conservative + * here, allowing only 255-block transfers in case an + * ancient device gets confused by length == 0. A length of 0 + * in a 10-byte read/write actually means 0 blocks. + */ + if (sd->flags & SDF_ANCIENT) { + max = sd->sc_dk.dk_label.d_secsize * 0xff; + + if (bp->b_bcount > max) + bp->b_bcount = max; + } + + (*sd->sc_link->adapter->scsi_minphys)(bp); +} + int sdread(dev, uio) dev_t dev; struct uio *uio; { - struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; - return (physio(sdstrategy, NULL, dev, B_READ, - sd->sc_link->adapter->scsi_minphys, uio)); + return (physio(sdstrategy, NULL, dev, B_READ, sdminphys, uio)); } int @@ -560,10 +619,8 @@ sdwrite(dev, uio) dev_t dev; struct uio *uio; { - struct sd_softc *sd = sdcd.cd_devs[SDUNIT(dev)]; - return (physio(sdstrategy, NULL, dev, B_WRITE, - sd->sc_link->adapter->scsi_minphys, uio)); + return (physio(sdstrategy, NULL, dev, B_WRITE, sdminphys, uio)); } /* |