diff options
author | David Gwynne <dlg@cvs.openbsd.org> | 2010-04-23 01:39:06 +0000 |
---|---|---|
committer | David Gwynne <dlg@cvs.openbsd.org> | 2010-04-23 01:39:06 +0000 |
commit | c594a08b404d4d96633d2c77a7ad4de807bf311c (patch) | |
tree | ca77958f5bbd408d912944f3f96966c94cb6fbe5 | |
parent | 70c6ccf8b3da5371f9d74d17ee48bc9707553a56 (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
-rw-r--r-- | sys/dev/ata/atascsi.c | 236 | ||||
-rw-r--r-- | sys/scsi/scsi_all.h | 63 | ||||
-rw-r--r-- | sys/scsi/scsi_ioctl.c | 95 |
3 files changed, 298 insertions, 96 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) { diff --git a/sys/scsi/scsi_all.h b/sys/scsi/scsi_all.h index e272a9ac97d..66006179153 100644 --- a/sys/scsi/scsi_all.h +++ b/sys/scsi/scsi_all.h @@ -1,4 +1,4 @@ -/* $OpenBSD: scsi_all.h,v 1.47 2009/10/12 00:30:15 dlg Exp $ */ +/* $OpenBSD: scsi_all.h,v 1.48 2010/04/23 01:39:05 dlg Exp $ */ /* $NetBSD: scsi_all.h,v 1.10 1996/09/12 01:57:17 thorpej Exp $ */ /* @@ -454,6 +454,67 @@ struct scsi_report_luns_data { #define RPL_LUNDATA_T0LUN 1 /* Type 0 LUN is in lundata[1] */ /* + * ATA PASS-THROUGH as per SAT2 + */ + +#define ATA_PASSTHRU_12 0xa1 +#define ATA_PASSTHRU_16 0x85 + +#define ATA_PASSTHRU_PROTO_MASK 0x1e +#define ATA_PASSTHRU_PROTO_HW_RESET 0x00 +#define ATA_PASSTHRU_PROTO_SW_RESET 0x02 +#define ATA_PASSTHRU_PROTO_NON_DATA 0x06 +#define ATA_PASSTHRU_PROTO_PIO_DATAIN 0x08 +#define ATA_PASSTHRU_PROTO_PIO_DATAOUT 0x0a +#define ATA_PASSTHRU_PROTO_DMA 0x0c +#define ATA_PASSTHRU_PROTO_DMA_QUEUED 0x0e +#define ATA_PASSTHRU_PROTO_EXEC_DIAG 0x10 +#define ATA_PASSTHRU_PROTO_NON_DATA_RST 0x12 +#define ATA_PASSTHRU_PROTO_UDMA_DATAIN 0x14 +#define ATA_PASSTHRU_PROTO_UDMA_DATAOUT 0x16 +#define ATA_PASSTHRU_PROTO_FPDMA 0x18 +#define ATA_PASSTHRU_PROTO_RESPONSE 0x1e + +#define ATA_PASSTHRU_T_DIR_MASK 0x08 +#define ATA_PASSTHRU_T_DIR_READ 0x08 +#define ATA_PASSTHRU_T_DIR_WRITE 0x00 + +#define ATA_PASSTHRU_T_LEN_MASK 0x03 +#define ATA_PASSTHRU_T_LEN_NONE 0x00 +#define ATA_PASSTHRU_T_LEN_FEATURES 0x01 +#define ATA_PASSTHRU_T_LEN_SECTOR_COUNT 0x02 +#define ATA_PASSTHRU_T_LEN_TPSIU 0x03 + +struct scsi_ata_passthru_12 { + u_int8_t opcode; + u_int8_t count_proto; + u_int8_t flags; + u_int8_t features; + u_int8_t sector_count; + u_int8_t lba_low; + u_int8_t lba_mid; + u_int8_t lba_high; + u_int8_t device; + u_int8_t command; + u_int8_t _reserved; + u_int8_t control; +}; + +struct scsi_ata_passthru_16 { + u_int8_t opcode; + u_int8_t count_proto; + u_int8_t flags; + u_int8_t features[2]; + u_int8_t sector_count[2]; + u_int8_t lba_low[2]; + u_int8_t lba_mid[2]; + u_int8_t lba_high[2]; + u_int8_t device; + u_int8_t command; + u_int8_t control; +}; + +/* * SPI status information unit. See section 14.3.5 of SPI-3. */ struct scsi_status_iu_header { diff --git a/sys/scsi/scsi_ioctl.c b/sys/scsi/scsi_ioctl.c index 58cc199333a..00114169853 100644 --- a/sys/scsi/scsi_ioctl.c +++ b/sys/scsi/scsi_ioctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: scsi_ioctl.c,v 1.40 2010/02/27 00:03:53 krw Exp $ */ +/* $OpenBSD: scsi_ioctl.c,v 1.41 2010/04/23 01:39:05 dlg Exp $ */ /* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */ /* @@ -50,9 +50,12 @@ #include <scsi/scsi_all.h> #include <scsi/scsiconf.h> + #include <sys/scsiio.h> +#include <sys/ataio.h> int scsi_ioc_cmd(struct scsi_link *, scsireq_t *); +int scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *); const unsigned char scsi_readsafe_cmd[256] = { [0x00] = 1, /* TEST UNIT READY */ @@ -183,6 +186,93 @@ err: return (err); } +int +scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq) +{ + struct scsi_xfer *xs; + struct scsi_ata_passthru_12 *cdb; + int err = 0; + + xs = scsi_xs_get(link, 0); + if (xs == NULL) + return (ENOMEM); + + cdb = (struct scsi_ata_passthru_12 *)xs->cmd; + cdb->opcode = ATA_PASSTHRU_12; + + if (atareq->datalen > 0) { + if (atareq->flags & ATACMD_READ) { + cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN; + cdb->flags = ATA_PASSTHRU_T_DIR_READ; + } else { + cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT; + cdb->flags = ATA_PASSTHRU_T_DIR_WRITE; + } + cdb->flags |= ATA_PASSTHRU_T_LEN_SECTOR_COUNT; + } else { + cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA; + cdb->flags = ATA_PASSTHRU_T_LEN_NONE; + } + cdb->features = atareq->features; + cdb->sector_count = atareq->sec_count; + cdb->lba_low = atareq->sec_num; + cdb->lba_mid = atareq->cylinder; + cdb->lba_high = atareq->cylinder >> 8; + cdb->device = atareq->head & 0x0f; + cdb->command = atareq->command; + + xs->cmdlen = sizeof(*cdb); + + if (atareq->datalen > 0) { + xs->data = malloc(atareq->datalen, M_TEMP, M_WAITOK); + xs->datalen = atareq->datalen; + } + + if (atareq->flags & ATACMD_READ) + xs->flags |= SCSI_DATA_IN; + if (atareq->flags & ATACMD_WRITE) { + if (atareq->datalen > 0) { + err = copyin(atareq->databuf, xs->data, + atareq->datalen); + if (err != 0) + goto err; + } + + xs->flags |= SCSI_DATA_OUT; + } + + xs->flags |= SCSI_SILENT; /* User is responsible for errors. */ + xs->retries = 0; /* user must do the retries *//* ignored */ + + scsi_xs_sync(xs); + + atareq->retsts = ATACMD_ERROR; + switch (xs->error) { + case XS_SENSE: + case XS_SHORTSENSE: + /* XXX this is not right */ + case XS_NOERROR: + atareq->retsts = ATACMD_OK; + break; + default: + atareq->retsts = ATACMD_ERROR; + break; + } + + if (atareq->datalen > 0 && atareq->flags & ATACMD_READ) { + err = copyout(xs->data, atareq->databuf, atareq->datalen); + if (err != 0) + goto err; + } + +err: + if (atareq->datalen > 0) + free(xs->data, M_TEMP); + scsi_xs_put(xs); + + return (err); +} + /* * Something (e.g. another driver) has called us * with an sc_link for a target/lun/adapter, and a scsi @@ -215,6 +305,7 @@ scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr, if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]]) break; /* FALLTHROUGH */ + case ATAIOCCOMMAND: case SCIOCDEBUG: if ((flag & FWRITE) == 0) return (EPERM); @@ -230,6 +321,8 @@ scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr, switch(cmd) { case SCIOCCOMMAND: return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr)); + case ATAIOCCOMMAND: + return (scsi_ioc_ata_cmd(sc_link, (atareq_t *)addr)); case SCIOCDEBUG: { int level = *((int *)addr); |