summaryrefslogtreecommitdiff
path: root/sys/dev/ata
diff options
context:
space:
mode:
authorDavid Gwynne <dlg@cvs.openbsd.org>2010-04-23 01:39:06 +0000
committerDavid Gwynne <dlg@cvs.openbsd.org>2010-04-23 01:39:06 +0000
commitc594a08b404d4d96633d2c77a7ad4de807bf311c (patch)
treeca77958f5bbd408d912944f3f96966c94cb6fbe5 /sys/dev/ata
parent70c6ccf8b3da5371f9d74d17ee48bc9707553a56 (diff)
the SAT spec from t10.org defines messages for tunnelling ATA commands over
SCSI when you're talking to a scsi to ata translation layer (satl). this implements the ata command ioctl handler in scsi_ioctl.c that wraps the ata command requests up in these scsi commands and then issues them against the hardware. this cuts atascsi over to interpreting these scsi commands rather than the ioctls directly. this should allow mpi users (with a relatively recent firmware) to use atactl against their SATA disks. i cant test cos my mpi(4) parts are too old to either support these scsi commands or too old to take recent firmwares. ok marco@ @krw tested by krw@ and me on ahci/atascsi
Diffstat (limited to 'sys/dev/ata')
-rw-r--r--sys/dev/ata/atascsi.c236
1 files changed, 142 insertions, 94 deletions
diff --git a/sys/dev/ata/atascsi.c b/sys/dev/ata/atascsi.c
index 386ed3695b8..33eb01a3005 100644
--- a/sys/dev/ata/atascsi.c
+++ b/sys/dev/ata/atascsi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: atascsi.c,v 1.81 2010/04/22 00:58:32 dlg Exp $ */
+/* $OpenBSD: atascsi.c,v 1.82 2010/04/23 01:39:05 dlg Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -59,8 +59,6 @@ struct atascsi_port {
};
void atascsi_cmd(struct scsi_xfer *);
-int atascsi_ioctl(struct scsi_link *, u_long, caddr_t, int,
- struct proc *);
int atascsi_probe(struct scsi_link *);
void atascsi_free(struct scsi_link *);
@@ -70,7 +68,7 @@ struct scsi_adapter atascsi_switch = {
scsi_minphys, /* scsi_minphys */
atascsi_probe, /* dev_probe */
atascsi_free, /* dev_free */
- atascsi_ioctl /* ioctl */
+ NULL, /* ioctl */
};
struct scsi_device atascsi_device = {
@@ -97,6 +95,11 @@ void atascsi_disk_sense(struct scsi_xfer *);
void atascsi_atapi_cmd(struct scsi_xfer *);
void atascsi_atapi_cmd_done(struct ata_xfer *);
+void atascsi_passthru_12(struct scsi_xfer *);
+void atascsi_passthru_16(struct scsi_xfer *);
+int atascsi_passthru_map(struct scsi_xfer *, u_int8_t, u_int8_t);
+void atascsi_passthru_done(struct ata_xfer *);
+
void atascsi_done(struct scsi_xfer *, int);
void ata_exec(struct atascsi *, struct ata_xfer *);
@@ -427,6 +430,13 @@ atascsi_disk_cmd(struct scsi_xfer *xs)
atascsi_disk_capacity16(xs);
return;
+ case ATA_PASSTHRU_12:
+ atascsi_passthru_12(xs);
+ return;
+ case ATA_PASSTHRU_16:
+ atascsi_passthru_16(xs);
+ return;
+
case TEST_UNIT_READY:
case START_STOP:
case PREVENT_ALLOW:
@@ -877,6 +887,134 @@ atascsi_disk_capacity16(struct scsi_xfer *xs)
atascsi_done(xs, XS_NOERROR);
}
+int
+atascsi_passthru_map(struct scsi_xfer *xs, u_int8_t count_proto, u_int8_t flags)
+{
+ struct ata_xfer *xa = xs->io;
+
+ xa->data = xs->data;
+ xa->datalen = xs->datalen;
+ xa->timeout = xs->timeout;
+ xa->flags = 0;
+ if (xs->flags & SCSI_DATA_IN)
+ xa->flags |= ATA_F_READ;
+ if (xs->flags & SCSI_DATA_OUT)
+ xa->flags |= ATA_F_WRITE;
+ if (xs->flags & SCSI_POLL)
+ xa->flags |= ATA_F_POLL;
+
+ switch (count_proto & ATA_PASSTHRU_PROTO_MASK) {
+ case ATA_PASSTHRU_PROTO_NON_DATA:
+ case ATA_PASSTHRU_PROTO_PIO_DATAIN:
+ case ATA_PASSTHRU_PROTO_PIO_DATAOUT:
+ xa->flags |= ATA_F_PIO;
+ break;
+ default:
+ /* we dont support this yet */
+ return (1);
+ }
+
+ xa->atascsi_private = xs;
+ xa->complete = atascsi_passthru_done;
+
+ return (0);
+}
+
+void
+atascsi_passthru_12(struct scsi_xfer *xs)
+{
+ struct scsi_link *link = xs->sc_link;
+ struct atascsi *as = link->adapter_softc;
+ struct ata_xfer *xa = xs->io;
+ struct scsi_ata_passthru_12 *cdb;
+ struct ata_fis_h2d *fis;
+
+ cdb = (struct scsi_ata_passthru_12 *)xs->cmd;
+ /* validate cdb */
+
+ if (atascsi_passthru_map(xs, cdb->count_proto, cdb->flags) != 0) {
+ atascsi_done(xs, XS_DRIVER_STUFFUP);
+ return;
+ }
+
+ fis = xa->fis;
+ fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->command = cdb->command;
+ fis->features = cdb->features;
+ fis->lba_low = cdb->lba_low;
+ fis->lba_mid = cdb->lba_mid;
+ fis->lba_high = cdb->lba_high;
+ fis->device = cdb->device;
+ fis->sector_count = cdb->sector_count;
+
+ ata_exec(as, xa);
+}
+
+void
+atascsi_passthru_16(struct scsi_xfer *xs)
+{
+ struct scsi_link *link = xs->sc_link;
+ struct atascsi *as = link->adapter_softc;
+ struct ata_xfer *xa = xs->io;
+ struct scsi_ata_passthru_16 *cdb;
+ struct ata_fis_h2d *fis;
+
+ cdb = (struct scsi_ata_passthru_16 *)xs->cmd;
+ /* validate cdb */
+
+ if (atascsi_passthru_map(xs, cdb->count_proto, cdb->flags) != 0) {
+ atascsi_done(xs, XS_DRIVER_STUFFUP);
+ return;
+ }
+
+ fis = xa->fis;
+ fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->command = cdb->command;
+ fis->features = cdb->features[1];
+ fis->lba_low = cdb->lba_low[1];
+ fis->lba_mid = cdb->lba_mid[1];
+ fis->lba_high = cdb->lba_high[1];
+ fis->device = cdb->device;
+ fis->lba_low_exp = cdb->lba_low[0];
+ fis->lba_mid_exp = cdb->lba_mid[0];
+ fis->lba_high_exp = cdb->lba_high[0];
+ fis->features_exp = cdb->features[0];
+ fis->sector_count = cdb->sector_count[1];
+ fis->sector_count_exp = cdb->sector_count[0];
+
+ ata_exec(as, xa);
+}
+
+void
+atascsi_passthru_done(struct ata_xfer *xa)
+{
+ struct scsi_xfer *xs = xa->atascsi_private;
+
+ /*
+ * XXX need to generate sense if cdb wants it
+ */
+
+ switch (xa->state) {
+ case ATA_S_COMPLETE:
+ xs->error = XS_NOERROR;
+ break;
+ case ATA_S_ERROR:
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ case ATA_S_TIMEOUT:
+ printf("atascsi_passthru_done, timeout\n");
+ xs->error = XS_TIMEOUT;
+ break;
+ default:
+ panic("atascsi_atapi_cmd_done: unexpected ata_xfer state (%d)",
+ xa->state);
+ }
+
+ xs->resid = xa->resid;
+
+ scsi_done(xs);
+}
+
void
atascsi_disk_sense(struct scsi_xfer *xs)
{
@@ -981,96 +1119,6 @@ atascsi_done(struct scsi_xfer *xs, int error)
splx(s);
}
-int atascsi_ioctl_cmd(struct atascsi *, struct atascsi_port *, atareq_t *);
-void atascsi_ioctl_done(struct ata_xfer *);
-
-int
-atascsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flags,
- struct proc *p)
-{
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
-
- switch (cmd) {
- case ATAIOCCOMMAND:
- return (atascsi_ioctl_cmd(as, ap, (atareq_t *)addr));
- default:
- return (ENOTTY);
- }
-}
-
-int
-atascsi_ioctl_cmd(struct atascsi *as, struct atascsi_port *ap, atareq_t *atareq)
-{
- struct ata_xfer *xa;
- struct ata_fis_h2d *fis;
- void *buf;
- int rc = 0;
- int s;
-
- xa = scsi_io_get(&ap->ap_iopool, 0);
-
- fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
- fis->command = atareq->command;
- fis->features = atareq->features;
- fis->lba_low = atareq->sec_num;
- fis->lba_mid = atareq->cylinder;
- fis->lba_high = atareq->cylinder >> 8;
- fis->device = atareq->head & 0x0f;
- fis->sector_count = atareq->sec_count;
-
- buf = malloc(atareq->datalen, M_TEMP, M_WAITOK);
-
- xa->data = buf;
- xa->datalen = atareq->datalen;
- xa->complete = atascsi_ioctl_done;
- xa->timeout = atareq->timeout;
- xa->flags = 0;
- if (atareq->flags & ATACMD_READ)
- xa->flags |= ATA_F_READ;
- if (atareq->flags & ATACMD_WRITE) {
- xa->flags |= ATA_F_WRITE;
- copyin(atareq->databuf, buf, atareq->datalen);
- }
- xa->atascsi_private = NULL;
-
- as->as_methods->ata_cmd(xa);
- s = splbio();
- while (!ISSET(xa->flags, ATA_F_DONE))
- tsleep(xa, PRIBIO, "atascsi", 0);
- splx(s);
-
- switch (xa->state) {
- case ATA_S_COMPLETE:
- atareq->retsts = ATACMD_OK;
- if (atareq->flags & ATACMD_READ)
- rc = copyout(buf, atareq->databuf, atareq->datalen);
- break;
- case ATA_S_ERROR:
- atareq->retsts = ATACMD_ERROR;
- break;
- case ATA_S_TIMEOUT:
- atareq->retsts = ATACMD_TIMEOUT;
- break;
- default:
- panic("atascsi_ioctl_cmd: unexpected ata_xfer state (%d)",
- xa->state);
- }
-
- free(buf, M_TEMP);
-
- scsi_io_put(&ap->ap_iopool, xa);
-
- return (rc);
-}
-
-void
-atascsi_ioctl_done(struct ata_xfer *xa)
-{
- wakeup(xa);
-}
-
void
ata_exec(struct atascsi *as, struct ata_xfer *xa)
{