summaryrefslogtreecommitdiff
path: root/sys/dev/ata/atascsi.c
diff options
context:
space:
mode:
authorDale Rahn <drahn@cvs.openbsd.org>2011-01-26 21:41:01 +0000
committerDale Rahn <drahn@cvs.openbsd.org>2011-01-26 21:41:01 +0000
commitc2a07274c68726b615403e10632b43beb8eac23d (patch)
treeeecf311d61eff5cac481c733225373a69df908bf /sys/dev/ata/atascsi.c
parentc39e9b783d98196c42dbb88a452736eb8bc25f28 (diff)
Add port multiplier support, has been in snaps for a while with no reported
issues. No actual OKs, but general acknowledgement and 'get it in' from several.
Diffstat (limited to 'sys/dev/ata/atascsi.c')
-rw-r--r--sys/dev/ata/atascsi.c448
1 files changed, 337 insertions, 111 deletions
diff --git a/sys/dev/ata/atascsi.c b/sys/dev/ata/atascsi.c
index dbd0c6e98ad..3d19bbc7133 100644
--- a/sys/dev/ata/atascsi.c
+++ b/sys/dev/ata/atascsi.c
@@ -1,7 +1,9 @@
-/* $OpenBSD: atascsi.c,v 1.99 2011/01/12 21:00:04 kettenis Exp $ */
+/* $OpenBSD: atascsi.c,v 1.100 2011/01/26 21:41:00 drahn Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
+ * Copyright (c) 2010 Conformal Systems LLC <info@conformal.com>
+ * Copyright (c) 2010 Jonathan Matthew <jonathan@d14n.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -30,6 +32,7 @@
#include <scsi/scsiconf.h>
#include <dev/ata/atascsi.h>
+#include <dev/ata/pmreg.h>
#include <sys/ataio.h>
@@ -39,7 +42,7 @@ struct atascsi {
struct device *as_dev;
void *as_cookie;
- struct atascsi_port **as_ports;
+ struct atascsi_host_port **as_host_ports;
struct atascsi_methods *as_methods;
struct scsi_adapter as_switch;
@@ -50,12 +53,40 @@ struct atascsi {
int as_ncqdepth;
};
+/*
+ * atascsi_host_port is a port attached to the host controller, and
+ * only holds the details relevant to the host controller.
+ * atascsi_port is any port, including ports on port multipliers, and
+ * it holds details of the device attached to the port.
+ *
+ * When there is a port multiplier attached to a port, the ahp_ports
+ * array in the atascsi_host_port struct contains one atascsi_port for
+ * each port, and one for the control port (port 15). The index into
+ * the array is the LUN used to address the port. For the control port,
+ * the LUN is 0, and for the port multiplier ports, the LUN is the
+ * port number plus one.
+ *
+ * When there is no port multiplier attached to a port, the ahp_ports
+ * array contains a single entry for the device. The LUN and port number
+ * for this entry are both 0.
+ */
+
+struct atascsi_host_port {
+ struct scsi_iopool ahp_iopool;
+ struct atascsi *ahp_as;
+ int ahp_port;
+ int ahp_nports;
+
+ struct atascsi_port **ahp_ports;
+};
+
struct atascsi_port {
struct ata_identify ap_identify;
- struct scsi_iopool ap_iopool;
+ struct atascsi_host_port *ap_host_port;
struct atascsi *ap_as;
- int ap_port;
+ int ap_pmp_port;
int ap_type;
+ int ap_ncqdepth;
int ap_features;
#define ATA_PORT_F_NCQ 0x1
#define ATA_PORT_F_TRIM 0x2
@@ -98,6 +129,12 @@ void atascsi_disk_start_stop_done(struct ata_xfer *);
void atascsi_atapi_cmd(struct scsi_xfer *);
void atascsi_atapi_cmd_done(struct ata_xfer *);
+void atascsi_pmp_cmd(struct scsi_xfer *);
+void atascsi_pmp_cmd_done(struct ata_xfer *);
+void atascsi_pmp_sense(struct scsi_xfer *xs);
+void atascsi_pmp_inq(struct scsi_xfer *xs);
+
+
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);
@@ -117,6 +154,7 @@ u_int ata_identify_block_logical_align(struct ata_identify *);
void *atascsi_io_get(void *);
void atascsi_io_put(void *, void *);
+struct atascsi_port * atascsi_lookup_port(struct scsi_link *);
struct atascsi *
atascsi_attach(struct device *self, struct atascsi_attach_args *aaa)
@@ -141,12 +179,12 @@ atascsi_attach(struct device *self, struct atascsi_attach_args *aaa)
as->as_link.adapter = &as->as_switch;
as->as_link.adapter_softc = as;
as->as_link.adapter_buswidth = aaa->aaa_nports;
- as->as_link.luns = 1; /* XXX port multiplier as luns */
+ as->as_link.luns = SATA_PMP_MAX_PORTS;
as->as_link.adapter_target = aaa->aaa_nports;
as->as_link.openings = 1;
- as->as_ports = malloc(sizeof(struct atascsi_port *) * aaa->aaa_nports,
- M_DEVBUF, M_WAITOK | M_ZERO);
+ as->as_host_ports = malloc(sizeof(struct atascsi_host_port *) *
+ aaa->aaa_nports, M_DEVBUF, M_WAITOK | M_ZERO);
bzero(&saa, sizeof(saa));
saa.saa_sc_link = &as->as_link;
@@ -167,44 +205,71 @@ atascsi_detach(struct atascsi *as, int flags)
if (rv != 0)
return (rv);
- free(as->as_ports, M_DEVBUF);
+ free(as->as_host_ports, M_DEVBUF);
free(as, M_DEVBUF);
return (0);
}
int
-atascsi_probe_dev(struct atascsi *as, int port)
+atascsi_probe_dev(struct atascsi *as, int port, int lun)
{
- return (scsi_probe_target(as->as_scsibus, port));
+ if (lun == 0) {
+ return (scsi_probe_target(as->as_scsibus, port));
+ } else {
+ return (scsi_probe_lun(as->as_scsibus, port, lun));
+ }
}
int
-atascsi_detach_dev(struct atascsi *as, int port, int flags)
+atascsi_detach_dev(struct atascsi *as, int port, int lun, int flags)
+{
+ if (lun == 0) {
+ return (scsi_detach_target(as->as_scsibus, port, flags));
+ } else {
+ return (scsi_detach_lun(as->as_scsibus, port, lun, flags));
+ }
+}
+
+struct atascsi_port *
+atascsi_lookup_port(struct scsi_link *link)
{
- return (scsi_detach_target(as->as_scsibus, port, flags));
+ struct atascsi *as = link->adapter_softc;
+ struct atascsi_host_port *ahp;
+
+ if (link->target > as->as_link.adapter_buswidth)
+ return (NULL);
+
+ ahp = as->as_host_ports[link->target];
+ if (link->lun >= ahp->ahp_nports)
+ return (NULL);
+
+ return (ahp->ahp_ports[link->lun]);
}
int
atascsi_probe(struct scsi_link *link)
{
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap;
- struct ata_xfer *xa;
- struct ata_identify *identify;
- int port, type, qdepth;
- int rv;
- u_int16_t cmdset;
-
- /* revisit this when we do port multipliers */
- if (link->lun > 0)
- return (ENXIO);
+ struct atascsi *as = link->adapter_softc;
+ struct atascsi_host_port *ahp;
+ struct atascsi_port *ap;
+ struct ata_xfer *xa;
+ struct ata_identify *identify;
+ int port, type, qdepth;
+ int rv;
+ u_int16_t cmdset;
port = link->target;
if (port > as->as_link.adapter_buswidth)
return (ENXIO);
- type = as->as_methods->probe(as->as_cookie, port);
+ /* if this is a PMP port, check it's valid */
+ if (link->lun > 0) {
+ if (link->lun > as->as_host_ports[port]->ahp_nports)
+ return (ENXIO);
+ }
+
+ type = as->as_methods->probe(as->as_cookie, port, link->lun);
switch (type) {
case ATA_PORT_T_DISK:
break;
@@ -212,6 +277,14 @@ atascsi_probe(struct scsi_link *link)
link->flags |= SDEV_ATAPI;
link->quirks |= SDEV_ONLYBIG;
break;
+ case ATA_PORT_T_PM:
+ if (link->lun != 0) {
+ printf("%s.%d.%d: Port multipliers cannot be nested\n",
+ as->as_dev->dv_xname, port, link->lun);
+ rv = ENODEV;
+ goto unsupported;
+ }
+ break;
default:
rv = ENODEV;
goto unsupported;
@@ -219,45 +292,89 @@ atascsi_probe(struct scsi_link *link)
ap = malloc(sizeof(*ap), M_DEVBUF, M_WAITOK | M_ZERO);
ap->ap_as = as;
- ap->ap_port = port;
+
+ if (link->lun == 0) {
+ ahp = malloc(sizeof(*ahp), M_DEVBUF, M_WAITOK | M_ZERO);
+ ahp->ahp_as = as;
+ ahp->ahp_port = port;
+
+ scsi_iopool_init(&ahp->ahp_iopool, ahp, atascsi_io_get,
+ atascsi_io_put);
+
+ as->as_host_ports[port] = ahp;
+
+ if (type == ATA_PORT_T_PM) {
+ ahp->ahp_nports = SATA_PMP_MAX_PORTS;
+ ap->ap_pmp_port = SATA_PMP_CONTROL_PORT;
+ } else {
+ ahp->ahp_nports = 1;
+ ap->ap_pmp_port = 0;
+ }
+ ahp->ahp_ports = malloc(sizeof(struct atascsi_port *) *
+ ahp->ahp_nports, M_DEVBUF, M_WAITOK | M_ZERO);
+ } else {
+ ahp = as->as_host_ports[port];
+ ap->ap_pmp_port = link->lun - 1;
+ }
+
+ ap->ap_host_port = ahp;
ap->ap_type = type;
- scsi_iopool_init(&ap->ap_iopool, ap, atascsi_io_get, atascsi_io_put);
- link->pool = &ap->ap_iopool;
+ link->pool = &ahp->ahp_iopool;
+
+ /* fetch the device info, except for port multipliers */
+ if (type != ATA_PORT_T_PM) {
+
+ /* devices attached to port multipliers tend not to be
+ * spun up at this point, and sometimes this prevents
+ * identification from working, so we retry a few times
+ * with a fairly long delay.
+ */
+ int count = 5;
+ do {
+ xa = scsi_io_get(&ahp->ahp_iopool, SCSI_NOSLEEP);
+ if (xa == NULL)
+ panic("no free xfers on a new port");
+ /* XXX dma reachable */
+ identify = malloc(sizeof(*identify), M_TEMP, M_WAITOK);
+ xa->pmp_port = ap->ap_pmp_port;
+ xa->data = identify;
+ xa->datalen = sizeof(*identify);
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
+ xa->fis->command = (type == ATA_PORT_T_DISK) ?
+ ATA_C_IDENTIFY : ATA_C_IDENTIFY_PACKET;
+ xa->fis->device = 0;
+ xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
+ xa->timeout = 1000;
+ xa->complete = ata_polled_complete;
+ xa->atascsi_private = &ahp->ahp_iopool;
+ ata_exec(as, xa);
+ rv = ata_polled(xa);
+ if (rv == 0) {
+ bcopy(identify, &ap->ap_identify,
+ sizeof(ap->ap_identify));
+ free(identify, M_TEMP);
+ break;
+ }
+ free(identify, M_TEMP);
+ delay(5000000);
+ } while (count--);
- /* fetch the device info */
- xa = scsi_io_get(&ap->ap_iopool, SCSI_NOSLEEP);
- if (xa == NULL)
- panic("no free xfers on a new port");
- identify = malloc(sizeof(*identify), M_TEMP, M_WAITOK); /* XXX dma reachable */
- xa->data = identify;
- xa->datalen = sizeof(*identify);
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
- xa->fis->command = (type == ATA_PORT_T_DISK) ?
- ATA_C_IDENTIFY : ATA_C_IDENTIFY_PACKET;
- xa->fis->device = 0;
- xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
- xa->timeout = 1000;
- xa->complete = ata_polled_complete;
- xa->atascsi_private = &ap->ap_iopool;
- ata_exec(as, xa);
- rv = ata_polled(xa);
- if (rv != 0) {
- free(identify, M_TEMP);
- goto error;
+ if (rv != 0) {
+ goto error;
+ }
}
- bcopy(identify, &ap->ap_identify, sizeof(ap->ap_identify));
- free(identify, M_TEMP);
- as->as_ports[port] = ap;
+ ahp->ahp_ports[link->lun] = ap;
if (type != ATA_PORT_T_DISK)
return (0);
if (as->as_capability & ASAA_CAP_NCQ &&
- ISSET(letoh16(ap->ap_identify.satacap), ATA_SATACAP_NCQ)) {
- qdepth = ATA_QDEPTH(letoh16(ap->ap_identify.qdepth));
- qdepth = MIN(qdepth, as->as_ncqdepth);
+ ISSET(letoh16(ap->ap_identify.satacap), ATA_SATACAP_NCQ) &&
+ (link->lun == 0 || as->as_capability & ASAA_CAP_PMP_NCQ)) {
+ ap->ap_ncqdepth = ATA_QDEPTH(letoh16(ap->ap_identify.qdepth));
+ qdepth = MIN(ap->ap_ncqdepth, as->as_ncqdepth);
if (ISSET(as->as_capability, ASAA_CAP_NEEDS_RESERVED))
qdepth--;
@@ -268,14 +385,16 @@ atascsi_probe(struct scsi_link *link)
link->openings = qdepth;
/*
- * XXX throw away any xfers that have tag numbers
- * higher than what the device supports.
+ * XXX for directly attached devices, throw away any xfers
+ * that have tag numbers higher than what the device supports.
*/
- while (qdepth--) {
- xa = scsi_io_get(&ap->ap_iopool, SCSI_NOSLEEP);
- if (xa->tag < link->openings) {
- xa->state = ATA_S_COMPLETE;
- scsi_io_put(&ap->ap_iopool, xa);
+ if (link->lun == 0) {
+ while (qdepth--) {
+ xa = scsi_io_get(&ahp->ahp_iopool, SCSI_NOSLEEP);
+ if (xa->tag < link->openings) {
+ xa->state = ATA_S_COMPLETE;
+ scsi_io_put(&ahp->ahp_iopool, xa);
+ }
}
}
}
@@ -289,32 +408,34 @@ atascsi_probe(struct scsi_link *link)
/* Enable write cache if supported */
if (ISSET(cmdset, ATA_IDENTIFY_WRITECACHE)) {
- xa = scsi_io_get(&ap->ap_iopool, SCSI_NOSLEEP);
+ xa = scsi_io_get(&ahp->ahp_iopool, SCSI_NOSLEEP);
if (xa == NULL)
panic("no free xfers on a new port");
xa->fis->command = ATA_C_SET_FEATURES;
xa->fis->features = ATA_SF_WRITECACHE_EN;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
xa->timeout = 1000;
xa->complete = ata_polled_complete;
- xa->atascsi_private = &ap->ap_iopool;
+ xa->pmp_port = ap->ap_pmp_port;
+ xa->atascsi_private = &ahp->ahp_iopool;
ata_exec(as, xa);
ata_polled(xa); /* we dont care if it doesnt work */
}
/* Enable read lookahead if supported */
if (ISSET(cmdset, ATA_IDENTIFY_LOOKAHEAD)) {
- xa = scsi_io_get(&ap->ap_iopool, SCSI_NOSLEEP);
+ xa = scsi_io_get(&ahp->ahp_iopool, SCSI_NOSLEEP);
if (xa == NULL)
panic("no free xfers on a new port");
xa->fis->command = ATA_C_SET_FEATURES;
xa->fis->features = ATA_SF_LOOKAHEAD_EN;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
xa->timeout = 1000;
xa->complete = ata_polled_complete;
- xa->atascsi_private = &ap->ap_iopool;
+ xa->pmp_port = ap->ap_pmp_port;
+ xa->atascsi_private = &ahp->ahp_iopool;
ata_exec(as, xa);
ata_polled(xa); /* we dont care if it doesnt work */
}
@@ -326,15 +447,16 @@ atascsi_probe(struct scsi_link *link)
* checking if the device sends a command abort to tell us it doesn't
* support it
*/
- xa = scsi_io_get(&ap->ap_iopool, SCSI_NOSLEEP);
+ xa = scsi_io_get(&ahp->ahp_iopool, SCSI_NOSLEEP);
if (xa == NULL)
panic("no free xfers on a new port");
xa->fis->command = ATA_C_SEC_FREEZE_LOCK;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->flags = ATA_F_READ | ATA_F_PIO | ATA_F_POLL;
xa->timeout = 1000;
xa->complete = ata_polled_complete;
- xa->atascsi_private = &ap->ap_iopool;
+ xa->pmp_port = ap->ap_pmp_port;
+ xa->atascsi_private = &ahp->ahp_iopool;
ata_exec(as, xa);
ata_polled(xa); /* we dont care if it doesnt work */
@@ -342,41 +464,53 @@ atascsi_probe(struct scsi_link *link)
error:
free(ap, M_DEVBUF);
unsupported:
- as->as_methods->free(as->as_cookie, port);
+
+ as->as_methods->free(as->as_cookie, port, link->lun);
return (rv);
}
void
atascsi_free(struct scsi_link *link)
{
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap;
- int port;
-
- if (link->lun > 0)
- return;
+ struct atascsi *as = link->adapter_softc;
+ struct atascsi_host_port *ahp;
+ struct atascsi_port *ap;
+ int port;
port = link->target;
if (port > as->as_link.adapter_buswidth)
return;
- ap = as->as_ports[port];
- if (ap == NULL)
+ ahp = as->as_host_ports[port];
+ if (ahp == NULL)
return;
+ if (link->lun >= ahp->ahp_nports)
+ return;
+
+ ap = ahp->ahp_ports[link->lun];
free(ap, M_DEVBUF);
- as->as_ports[port] = NULL;
+ ahp->ahp_ports[link->lun] = NULL;
- as->as_methods->free(as->as_cookie, port);
+ as->as_methods->free(as->as_cookie, port, link->lun);
+
+ if (link->lun == ahp->ahp_nports - 1) {
+ /* we've already freed all of ahp->ahp_ports, now
+ * free ahp itself. this relies on the order luns are
+ * detached in scsi_detach_target().
+ */
+ free(ahp, M_DEVBUF);
+ as->as_host_ports[port] = NULL;
+ }
}
void
atascsi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
+ ap = atascsi_lookup_port(link);
if (ap == NULL) {
atascsi_done(xs, XS_DRIVER_STUFFUP);
return;
@@ -389,6 +523,9 @@ atascsi_cmd(struct scsi_xfer *xs)
case ATA_PORT_T_ATAPI:
atascsi_atapi_cmd(xs);
break;
+ case ATA_PORT_T_PM:
+ atascsi_pmp_cmd(xs);
+ break;
case ATA_PORT_T_NONE:
default:
@@ -402,13 +539,15 @@ atascsi_disk_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
int flags = 0;
struct ata_fis_h2d *fis;
u_int64_t lba;
u_int32_t sector_count;
+ ap = atascsi_lookup_port(link);
+
switch (xs->cmd->opcode) {
case READ_COMMAND:
case READ_BIG:
@@ -474,12 +613,13 @@ atascsi_disk_cmd(struct scsi_xfer *xs)
fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
fis->lba_low = lba & 0xff;
fis->lba_mid = (lba >> 8) & 0xff;
fis->lba_high = (lba >> 16) & 0xff;
if (ISSET(ap->ap_features, ATA_PORT_F_NCQ) &&
+ (xa->tag < ap->ap_ncqdepth) &&
!(xs->flags & SCSI_POLL)) {
/* Use NCQ */
xa->flags |= ATA_F_NCQ;
@@ -514,6 +654,7 @@ atascsi_disk_cmd(struct scsi_xfer *xs)
xa->datalen = xs->datalen;
xa->complete = atascsi_disk_cmd_done;
xa->timeout = xs->timeout;
+ xa->pmp_port = ap->ap_pmp_port;
xa->atascsi_private = xs;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
@@ -587,8 +728,9 @@ atascsi_disk_inquiry(struct scsi_xfer *xs)
{
struct scsi_inquiry_data inq;
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
+
+ ap = atascsi_lookup_port(link);
bzero(&inq, sizeof(inq));
@@ -636,10 +778,10 @@ void
atascsi_disk_vpd_serial(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_vpd_serial pg;
+ ap = atascsi_lookup_port(link);
bzero(&pg, sizeof(pg));
pg.hdr.device = T_DIRECT;
@@ -657,8 +799,7 @@ void
atascsi_disk_vpd_ident(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct {
struct scsi_vpd_hdr hdr;
struct scsi_vpd_devid_hdr devid_hdr;
@@ -667,6 +808,7 @@ atascsi_disk_vpd_ident(struct scsi_xfer *xs)
u_int8_t *p;
size_t pg_len;
+ ap = atascsi_lookup_port(link);
bzero(&pg, sizeof(pg));
if (letoh16(ap->ap_identify.features87) & ATA_ID_F87_WWN) {
pg_len = 8;
@@ -708,10 +850,10 @@ void
atascsi_disk_vpd_limits(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_vpd_disk_limits pg;
+ ap = atascsi_lookup_port(link);
bzero(&pg, sizeof(pg));
pg.hdr.device = T_DIRECT;
pg.hdr.page_code = SI_PG_DISK_LIMITS;
@@ -729,10 +871,10 @@ void
atascsi_disk_vpd_info(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_vpd_disk_info pg;
+ ap = atascsi_lookup_port(link);
bzero(&pg, sizeof(pg));
pg.hdr.device = T_DIRECT;
pg.hdr.page_code = SI_PG_DISK_INFO;
@@ -751,7 +893,7 @@ atascsi_disk_write_same_16(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_write_same_16 *cdb;
struct ata_xfer *xa = xs->io;
struct ata_fis_h2d *fis;
@@ -764,6 +906,7 @@ atascsi_disk_write_same_16(struct scsi_xfer *xs)
return;
}
+ ap = atascsi_lookup_port(link);
cdb = (struct scsi_write_same_16 *)xs->cmd;
if (cdb->flags != WRITE_SAME_F_UNMAP ||
@@ -785,6 +928,7 @@ atascsi_disk_write_same_16(struct scsi_xfer *xs)
xa->data = xs->data;
xa->datalen = xs->datalen;
xa->flags = ATA_F_WRITE;
+ xa->pmp_port = ap->ap_pmp_port;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
xa->complete = atascsi_disk_write_same_16_done;
@@ -797,7 +941,7 @@ atascsi_disk_write_same_16(struct scsi_xfer *xs)
memcpy(xa->data, &desc, sizeof(desc));
fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
fis->command = ATA_C_DSM;
fis->features = ATA_DSM_TRIM;
@@ -835,6 +979,7 @@ atascsi_disk_sync(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
if (xs->cmdlen != sizeof(struct scsi_synchronize_cache)) {
@@ -842,16 +987,18 @@ atascsi_disk_sync(struct scsi_xfer *xs)
return;
}
+ ap = atascsi_lookup_port(link);
xa->datalen = 0;
xa->flags = ATA_F_READ;
xa->complete = atascsi_disk_sync_done;
/* Spec says flush cache can take >30 sec, so give it at least 45. */
xa->timeout = (xs->timeout < 45000) ? 45000 : xs->timeout;
xa->atascsi_private = xs;
+ xa->pmp_port = ap->ap_pmp_port;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->fis->command = ATA_C_FLUSH_CACHE;
xa->fis->device = 0;
@@ -955,11 +1102,11 @@ void
atascsi_disk_capacity(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_read_cap_data rcd;
u_int64_t capacity;
+ ap = atascsi_lookup_port(link);
if (xs->cmdlen != sizeof(struct scsi_read_capacity)) {
atascsi_done(xs, XS_DRIVER_STUFFUP);
return;
@@ -982,12 +1129,12 @@ void
atascsi_disk_capacity16(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
- struct atascsi *as = link->adapter_softc;
- struct atascsi_port *ap = as->as_ports[link->target];
+ struct atascsi_port *ap;
struct scsi_read_cap_data_16 rcd;
u_int align;
u_int16_t lowest_aligned = 0;
+ ap = atascsi_lookup_port(link);
if (xs->cmdlen != sizeof(struct scsi_read_capacity_16)) {
atascsi_done(xs, XS_DRIVER_STUFFUP);
return;
@@ -1054,6 +1201,7 @@ atascsi_passthru_12(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
struct scsi_ata_passthru_12 *cdb;
struct ata_fis_h2d *fis;
@@ -1071,8 +1219,9 @@ atascsi_passthru_12(struct scsi_xfer *xs)
return;
}
+ ap = atascsi_lookup_port(link);
fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
fis->command = cdb->command;
fis->features = cdb->features;
fis->lba_low = cdb->lba_low;
@@ -1080,6 +1229,7 @@ atascsi_passthru_12(struct scsi_xfer *xs)
fis->lba_high = cdb->lba_high;
fis->device = cdb->device;
fis->sector_count = cdb->sector_count;
+ xa->pmp_port = ap->ap_pmp_port;
ata_exec(as, xa);
}
@@ -1089,6 +1239,7 @@ atascsi_passthru_16(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
struct scsi_ata_passthru_16 *cdb;
struct ata_fis_h2d *fis;
@@ -1106,8 +1257,9 @@ atascsi_passthru_16(struct scsi_xfer *xs)
return;
}
+ ap = atascsi_lookup_port(link);
fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
fis->command = cdb->command;
fis->features = cdb->features[1];
fis->lba_low = cdb->lba_low[1];
@@ -1120,6 +1272,7 @@ atascsi_passthru_16(struct scsi_xfer *xs)
fis->features_exp = cdb->features[0];
fis->sector_count = cdb->sector_count[1];
fis->sector_count_exp = cdb->sector_count[0];
+ xa->pmp_port = ap->ap_pmp_port;
ata_exec(as, xa);
}
@@ -1172,6 +1325,7 @@ atascsi_disk_start_stop(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
struct scsi_start_stop *ss = (struct scsi_start_stop *)xs->cmd;
@@ -1190,16 +1344,18 @@ atascsi_disk_start_stop(struct scsi_xfer *xs)
* zero gets translated into an ATA FLUSH CACHE command
* followed by an ATA STANDBY IMMEDIATE command.
*/
+ ap = atascsi_lookup_port(link);
xa->datalen = 0;
xa->flags = ATA_F_READ;
xa->complete = atascsi_disk_start_stop_done;
/* Spec says flush cache can take >30 sec, so give it at least 45. */
xa->timeout = (xs->timeout < 45000) ? 45000 : xs->timeout;
+ xa->pmp_port = ap->ap_pmp_port;
xa->atascsi_private = xs;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->fis->command = ATA_C_FLUSH_CACHE;
xa->fis->device = 0;
@@ -1212,6 +1368,7 @@ atascsi_disk_start_stop_done(struct ata_xfer *xa)
struct scsi_xfer *xs = xa->atascsi_private;
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
switch (xa->state) {
case ATA_S_COMPLETE:
@@ -1234,17 +1391,19 @@ atascsi_disk_start_stop_done(struct ata_xfer *xa)
* The FLUSH CACHE command completed succesfully; now issue
* the STANDBY IMMEDATE command.
*/
+ ap = atascsi_lookup_port(link);
xa->datalen = 0;
xa->flags = ATA_F_READ;
xa->state = ATA_S_SETUP;
xa->complete = atascsi_disk_cmd_done;
/* Spec says flush cache can take >30 sec, so give it at least 45. */
xa->timeout = (xs->timeout < 45000) ? 45000 : xs->timeout;
+ xa->pmp_port = ap->ap_pmp_port;
xa->atascsi_private = xs;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
- xa->fis->flags = ATA_H2D_FLAGS_CMD;
+ xa->fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
xa->fis->command = ATA_C_STANDBY_IMMED;
xa->fis->device = 0;
@@ -1256,6 +1415,7 @@ atascsi_atapi_cmd(struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
struct atascsi *as = link->adapter_softc;
+ struct atascsi_port *ap;
struct ata_xfer *xa = xs->io;
struct ata_fis_h2d *fis;
@@ -1269,17 +1429,20 @@ atascsi_atapi_cmd(struct scsi_xfer *xs)
default:
xa->flags = ATA_F_PACKET;
}
+ xa->flags |= ATA_F_GET_RFIS;
+ ap = atascsi_lookup_port(link);
xa->data = xs->data;
xa->datalen = xs->datalen;
xa->complete = atascsi_atapi_cmd_done;
xa->timeout = xs->timeout;
+ xa->pmp_port = ap->ap_pmp_port;
xa->atascsi_private = xs;
if (xs->flags & SCSI_POLL)
xa->flags |= ATA_F_POLL;
fis = xa->fis;
- fis->flags = ATA_H2D_FLAGS_CMD;
+ fis->flags = ATA_H2D_FLAGS_CMD | ap->ap_pmp_port;
fis->command = ATA_C_PACKET;
fis->device = 0;
fis->sector_count = xa->tag << 3;
@@ -1331,6 +1494,70 @@ atascsi_atapi_cmd_done(struct ata_xfer *xa)
}
void
+atascsi_pmp_cmd(struct scsi_xfer *xs)
+{
+ switch (xs->cmd->opcode) {
+ case REQUEST_SENSE:
+ atascsi_pmp_sense(xs);
+ return;
+ case INQUIRY:
+ atascsi_pmp_inq(xs);
+ return;
+
+ case TEST_UNIT_READY:
+ case PREVENT_ALLOW:
+ atascsi_done(xs, XS_NOERROR);
+ return;
+
+ default:
+ atascsi_done(xs, XS_DRIVER_STUFFUP);
+ return;
+ }
+}
+
+void
+atascsi_pmp_sense(struct scsi_xfer *xs)
+{
+ struct scsi_sense_data *sd = (struct scsi_sense_data *)xs->data;
+
+ bzero(xs->data, xs->datalen);
+ sd->error_code = SSD_ERRCODE_CURRENT;
+ sd->flags = SKEY_NO_SENSE;
+
+ atascsi_done(xs, XS_NOERROR);
+}
+
+void
+atascsi_pmp_inq(struct scsi_xfer *xs)
+{
+ struct scsi_inquiry_data inq;
+ struct scsi_inquiry *in_inq = (struct scsi_inquiry *)xs->cmd;
+
+ if (ISSET(in_inq->flags, SI_EVPD)) {
+ /* any evpd pages we need to support here? */
+ atascsi_done(xs, XS_DRIVER_STUFFUP);
+ return;
+ }
+
+ inq.device = 0x1E; /* "well known logical unit" seems reasonable */
+ inq.version = 0x05; /* SPC-3? */
+ inq.response_format = 2;
+ inq.additional_length = 32;
+ inq.flags |= SID_CmdQue;
+ bcopy("ATA ", inq.vendor, sizeof(inq.vendor));
+
+ /* should use the data from atascsi_pmp_identify here?
+ * not sure how useful the chip id is, but maybe it'd be
+ * nice to include the number of ports.
+ */
+ bcopy("Port Multiplier", inq.product, sizeof(inq.product));
+ bcopy(" ", inq.revision, sizeof(inq.revision));
+
+ bcopy(&inq, xs->data, MIN(sizeof(inq), xs->datalen));
+ atascsi_done(xs, XS_NOERROR);
+}
+
+void
atascsi_done(struct scsi_xfer *xs, int error)
{
xs->error = error;
@@ -1346,11 +1573,11 @@ ata_exec(struct atascsi *as, struct ata_xfer *xa)
void *
atascsi_io_get(void *cookie)
{
- struct atascsi_port *ap = cookie;
- struct atascsi *as = ap->ap_as;
- struct ata_xfer *xa;
+ struct atascsi_host_port *ahp = cookie;
+ struct atascsi *as = ahp->ahp_as;
+ struct ata_xfer *xa;
- xa = as->as_methods->ata_get_xfer(as->as_cookie, ap->ap_port);
+ xa = as->as_methods->ata_get_xfer(as->as_cookie, ahp->ahp_port);
if (xa != NULL)
xa->fis->type = ATA_FIS_TYPE_H2D;
@@ -1416,4 +1643,3 @@ ata_swapcopy(void *src, void *dst, size_t len)
for (i = 0; i < len; i++)
d[i] = swap16(s[i]);
}
-