summaryrefslogtreecommitdiff
path: root/sys/scsi
diff options
context:
space:
mode:
Diffstat (limited to 'sys/scsi')
-rw-r--r--sys/scsi/sd.c97
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));
}
/*