summaryrefslogtreecommitdiffstats
path: root/sys/dev/mps
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/mps')
-rw-r--r--sys/dev/mps/mps.c56
-rw-r--r--sys/dev/mps/mps_pci.c1
-rw-r--r--sys/dev/mps/mps_sas.c346
-rw-r--r--sys/dev/mps/mps_user.c2
-rw-r--r--sys/dev/mps/mpsvar.h16
5 files changed, 414 insertions, 7 deletions
diff --git a/sys/dev/mps/mps.c b/sys/dev/mps/mps.c
index 404b12e..1fb37e2 100644
--- a/sys/dev/mps/mps.c
+++ b/sys/dev/mps/mps.c
@@ -1569,17 +1569,53 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
sc = cm->cm_sc;
/*
- * Set up DMA direction flags. Note no support for
- * bi-directional transactions.
+ * In this case, just print out a warning and let the chip tell the
+ * user they did the wrong thing.
+ */
+ if ((cm->cm_max_segs != 0) && (nsegs > cm->cm_max_segs)) {
+ mps_printf(sc, "%s: warning: busdma returned %d segments, "
+ "more than the %d allowed\n", __func__, nsegs,
+ cm->cm_max_segs);
+ }
+
+ /*
+ * Set up DMA direction flags. Note that we don't support
+ * bi-directional transfers, with the exception of SMP passthrough.
*/
sflags = 0;
- if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
+ if (cm->cm_flags & MPS_CM_FLAGS_SMP_PASS) {
+ /*
+ * We have to add a special case for SMP passthrough, there
+ * is no easy way to generically handle it. The first
+ * S/G element is used for the command (therefore the
+ * direction bit needs to be set). The second one is used
+ * for the reply. We'll leave it to the caller to make
+ * sure we only have two buffers.
+ */
+ /*
+ * Even though the busdma man page says it doesn't make
+ * sense to have both direction flags, it does in this case.
+ * We have one s/g element being accessed in each direction.
+ */
+ dir = BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD;
+
+ /*
+ * Set the direction flag on the first buffer in the SMP
+ * passthrough request. We'll clear it for the second one.
+ */
+ sflags |= MPI2_SGE_FLAGS_DIRECTION |
+ MPI2_SGE_FLAGS_END_OF_BUFFER;
+ } else if (cm->cm_flags & MPS_CM_FLAGS_DATAOUT) {
sflags |= MPI2_SGE_FLAGS_DIRECTION;
dir = BUS_DMASYNC_PREWRITE;
} else
dir = BUS_DMASYNC_PREREAD;
for (i = 0; i < nsegs; i++) {
+ if ((cm->cm_flags & MPS_CM_FLAGS_SMP_PASS)
+ && (i != 0)) {
+ sflags &= ~MPI2_SGE_FLAGS_DIRECTION;
+ }
error = mps_add_dmaseg(cm, segs[i].ds_addr, segs[i].ds_len,
sflags, nsegs - i);
if (error != 0) {
@@ -1595,6 +1631,13 @@ mps_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
return;
}
+static void
+mps_data_cb2(void *arg, bus_dma_segment_t *segs, int nsegs, bus_size_t mapsize,
+ int error)
+{
+ mps_data_cb(arg, segs, nsegs, error);
+}
+
/*
* Note that the only error path here is from bus_dmamap_load(), which can
* return EINPROGRESS if it is waiting for resources.
@@ -1605,7 +1648,10 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm)
MPI2_SGE_SIMPLE32 *sge;
int error = 0;
- if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
+ if (cm->cm_flags & MPS_CM_FLAGS_USE_UIO) {
+ error = bus_dmamap_load_uio(sc->buffer_dmat, cm->cm_dmamap,
+ &cm->cm_uio, mps_data_cb2, cm, 0);
+ } else if ((cm->cm_data != NULL) && (cm->cm_length != 0)) {
error = bus_dmamap_load(sc->buffer_dmat, cm->cm_dmamap,
cm->cm_data, cm->cm_length, mps_data_cb, cm, 0);
} else {
@@ -1619,7 +1665,7 @@ mps_map_command(struct mps_softc *sc, struct mps_command *cm)
MPI2_SGE_FLAGS_SHIFT;
sge->Address = 0;
}
- mps_enqueue_request(sc, cm);
+ mps_enqueue_request(sc, cm);
}
return (error);
diff --git a/sys/dev/mps/mps_pci.c b/sys/dev/mps/mps_pci.c
index 2b33ba8..2c57286 100644
--- a/sys/dev/mps/mps_pci.c
+++ b/sys/dev/mps/mps_pci.c
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
+#include <sys/uio.h>
#include <machine/bus.h>
#include <machine/resource.h>
diff --git a/sys/dev/mps/mps_sas.c b/sys/dev/mps/mps_sas.c
index 5883e49..0e7ce03 100644
--- a/sys/dev/mps/mps_sas.c
+++ b/sys/dev/mps/mps_sas.c
@@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/uio.h>
#include <sys/sysctl.h>
+#include <sys/sglist.h>
+#include <sys/endian.h>
#include <machine/bus.h>
#include <machine/resource.h>
@@ -55,6 +57,9 @@ __FBSDID("$FreeBSD$");
#include <cam/cam_periph.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_message.h>
+#if __FreeBSD_version >= 900026
+#include <cam/scsi/smp_all.h>
+#endif
#include <dev/mps/mpi/mpi2_type.h>
#include <dev/mps/mpi/mpi2.h>
@@ -69,9 +74,11 @@ struct mpssas_target {
uint16_t handle;
uint8_t linkrate;
uint64_t devname;
+ uint64_t sasaddr;
uint32_t devinfo;
uint16_t encl_handle;
uint16_t encl_slot;
+ uint16_t parent_handle;
int flags;
#define MPSSAS_TARGET_INABORT (1 << 0)
#define MPSSAS_TARGET_INRESET (1 << 1)
@@ -144,6 +151,12 @@ static int mpssas_complete_tm_request(struct mps_softc *sc,
struct mps_command *cm, int free_cm);
static void mpssas_action_scsiio(struct mpssas_softc *, union ccb *);
static void mpssas_scsiio_complete(struct mps_softc *, struct mps_command *);
+#if __FreeBSD_version >= 900026
+static void mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm);
+static void mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb,
+ uint64_t sasaddr);
+static void mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb);
+#endif /* __FreeBSD_version >= 900026 */
static void mpssas_resetdev(struct mpssas_softc *, struct mps_command *);
static void mpssas_action_resetdev(struct mpssas_softc *, union ccb *);
static void mpssas_resetdev_complete(struct mps_softc *, struct mps_command *);
@@ -312,6 +325,8 @@ mpssas_probe_device_complete(struct mps_softc *sc,
probe->target.devinfo = buf->DeviceInfo;
probe->target.encl_handle = buf->EnclosureHandle;
probe->target.encl_slot = buf->Slot;
+ probe->target.sasaddr = mps_to_u64(&buf->SASAddress);
+ probe->target.parent_handle = buf->ParentDevHandle;
if (buf->DeviceInfo & MPI2_SAS_DEVICE_INFO_DIRECT_ATTACH) {
params->page_address =
@@ -916,6 +931,11 @@ mpssas_action(struct cam_sim *sim, union ccb *ccb)
case XPT_SCSI_IO:
mpssas_action_scsiio(sassc, ccb);
return;
+#if __FreeBSD_version >= 900026
+ case XPT_SMP_IO:
+ mpssas_action_smpio(sassc, ccb);
+ return;
+#endif /* __FreeBSD_version >= 900026 */
default:
ccb->ccb_h.status = CAM_FUNC_NOTAVAIL;
break;
@@ -1361,6 +1381,9 @@ mpssas_action_scsiio(struct mpssas_softc *sassc, union ccb *ccb)
bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len);
req->IoFlags = csio->cdb_len;
+ /*
+ * XXX need to handle S/G lists and physical addresses here.
+ */
cm->cm_data = csio->data_ptr;
cm->cm_length = csio->dxfer_len;
cm->cm_sge = &req->SGL;
@@ -1525,6 +1548,329 @@ mpssas_scsiio_complete(struct mps_softc *sc, struct mps_command *cm)
xpt_done(ccb);
}
+#if __FreeBSD_version >= 900026
+static void
+mpssas_smpio_complete(struct mps_softc *sc, struct mps_command *cm)
+{
+ MPI2_SMP_PASSTHROUGH_REPLY *rpl;
+ MPI2_SMP_PASSTHROUGH_REQUEST *req;
+ uint64_t sasaddr;
+ union ccb *ccb;
+
+ ccb = cm->cm_complete_data;
+ rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
+ if (rpl == NULL) {
+ mps_dprint(sc, MPS_INFO, "%s: NULL cm_reply!\n", __func__);
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ goto bailout;
+ }
+
+ req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+ sasaddr = le32toh(req->SASAddress.Low);
+ sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32;
+
+ if ((rpl->IOCStatus & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS ||
+ rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) {
+ mps_dprint(sc, MPS_INFO, "%s: IOCStatus %04x SASStatus %02x\n",
+ __func__, rpl->IOCStatus, rpl->SASStatus);
+ ccb->ccb_h.status = CAM_REQ_CMP_ERR;
+ goto bailout;
+ }
+
+ mps_dprint(sc, MPS_INFO, "%s: SMP request to SAS address "
+ "%#jx completed successfully\n", __func__,
+ (uintmax_t)sasaddr);
+
+ if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED)
+ ccb->ccb_h.status = CAM_REQ_CMP;
+ else
+ ccb->ccb_h.status = CAM_SMP_STATUS_ERROR;
+
+bailout:
+ /*
+ * We sync in both directions because we had DMAs in the S/G list
+ * in both directions.
+ */
+ bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
+ mps_free_command(sc, cm);
+ xpt_done(ccb);
+}
+
+static void
+mpssas_send_smpcmd(struct mpssas_softc *sassc, union ccb *ccb, uint64_t sasaddr)
+{
+ struct mps_command *cm;
+ uint8_t *request, *response;
+ MPI2_SMP_PASSTHROUGH_REQUEST *req;
+ struct mps_softc *sc;
+ struct sglist *sg;
+ int error;
+
+ sc = sassc->sc;
+ sg = NULL;
+ error = 0;
+
+ /*
+ * XXX We don't yet support physical addresses here.
+ */
+ if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) {
+ mps_printf(sc, "%s: physical addresses not supported\n",
+ __func__);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ /*
+ * If the user wants to send an S/G list, check to make sure they
+ * have single buffers.
+ */
+ if (ccb->ccb_h.flags & CAM_SCATTER_VALID) {
+ /*
+ * The chip does not support more than one buffer for the
+ * request or response.
+ */
+ if ((ccb->smpio.smp_request_sglist_cnt > 1)
+ || (ccb->smpio.smp_response_sglist_cnt > 1)) {
+ mps_printf(sc, "%s: multiple request or response "
+ "buffer segments not supported for SMP\n",
+ __func__);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ xpt_done(ccb);
+ return;
+ }
+
+ /*
+ * The CAM_SCATTER_VALID flag was originally implemented
+ * for the XPT_SCSI_IO CCB, which only has one data pointer.
+ * We have two. So, just take that flag to mean that we
+ * might have S/G lists, and look at the S/G segment count
+ * to figure out whether that is the case for each individual
+ * buffer.
+ */
+ if (ccb->smpio.smp_request_sglist_cnt != 0) {
+ bus_dma_segment_t *req_sg;
+
+ req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request;
+ request = (uint8_t *)req_sg[0].ds_addr;
+ } else
+ request = ccb->smpio.smp_request;
+
+ if (ccb->smpio.smp_response_sglist_cnt != 0) {
+ bus_dma_segment_t *rsp_sg;
+
+ rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response;
+ response = (uint8_t *)rsp_sg[0].ds_addr;
+ } else
+ response = ccb->smpio.smp_response;
+ } else {
+ request = ccb->smpio.smp_request;
+ response = ccb->smpio.smp_response;
+ }
+
+ cm = mps_alloc_command(sc);
+ if (cm == NULL) {
+ mps_printf(sc, "%s: cannot allocate command\n", __func__);
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(ccb);
+ return;
+ }
+
+ req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
+ bzero(req, sizeof(*req));
+ req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
+
+ /* Allow the chip to use any route to this SAS address. */
+ req->PhysicalPort = 0xff;
+
+ req->RequestDataLength = ccb->smpio.smp_request_len;
+ req->SGLFlags =
+ MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI;
+
+ mps_dprint(sc, MPS_INFO, "%s: sending SMP request to SAS "
+ "address %#jx\n", __func__, (uintmax_t)sasaddr);
+
+ mpi_init_sge(cm, req, &req->SGL);
+
+ /*
+ * Set up a uio to pass into mps_map_command(). This allows us to
+ * do one map command, and one busdma call in there.
+ */
+ cm->cm_uio.uio_iov = cm->cm_iovec;
+ cm->cm_uio.uio_iovcnt = 2;
+ cm->cm_uio.uio_segflg = UIO_SYSSPACE;
+
+ /*
+ * The read/write flag isn't used by busdma, but set it just in
+ * case. This isn't exactly accurate, either, since we're going in
+ * both directions.
+ */
+ cm->cm_uio.uio_rw = UIO_WRITE;
+
+ cm->cm_iovec[0].iov_base = request;
+ cm->cm_iovec[0].iov_len = req->RequestDataLength;
+ cm->cm_iovec[1].iov_base = response;
+ cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len;
+
+ cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len +
+ cm->cm_iovec[1].iov_len;
+
+ /*
+ * Trigger a warning message in mps_data_cb() for the user if we
+ * wind up exceeding two S/G segments. The chip expects one
+ * segment for the request and another for the response.
+ */
+ cm->cm_max_segs = 2;
+
+ cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
+ cm->cm_complete = mpssas_smpio_complete;
+ cm->cm_complete_data = ccb;
+
+ /*
+ * Tell the mapping code that we're using a uio, and that this is
+ * an SMP passthrough request. There is a little special-case
+ * logic there (in mps_data_cb()) to handle the bidirectional
+ * transfer.
+ */
+ cm->cm_flags |= MPS_CM_FLAGS_USE_UIO | MPS_CM_FLAGS_SMP_PASS |
+ MPS_CM_FLAGS_DATAIN | MPS_CM_FLAGS_DATAOUT;
+
+ /* The chip data format is little endian. */
+ req->SASAddress.High = htole32(sasaddr >> 32);
+ req->SASAddress.Low = htole32(sasaddr);
+
+ /*
+ * XXX Note that we don't have a timeout/abort mechanism here.
+ * From the manual, it looks like task management requests only
+ * work for SCSI IO and SATA passthrough requests. We may need to
+ * have a mechanism to retry requests in the event of a chip reset
+ * at least. Hopefully the chip will insure that any errors short
+ * of that are relayed back to the driver.
+ */
+ error = mps_map_command(sc, cm);
+ if ((error != 0) && (error != EINPROGRESS)) {
+ mps_printf(sc, "%s: error %d returned from mps_map_command()\n",
+ __func__, error);
+ goto bailout_error;
+ }
+
+ return;
+
+bailout_error:
+ mps_free_command(sc, cm);
+ ccb->ccb_h.status = CAM_RESRC_UNAVAIL;
+ xpt_done(ccb);
+ return;
+
+}
+
+static void
+mpssas_action_smpio(struct mpssas_softc *sassc, union ccb *ccb)
+{
+ struct mps_softc *sc;
+ struct mpssas_target *targ;
+ uint64_t sasaddr = 0;
+
+ sc = sassc->sc;
+
+ /*
+ * Make sure the target exists.
+ */
+ targ = &sassc->targets[ccb->ccb_h.target_id];
+ if (targ->handle == 0x0) {
+ mps_printf(sc, "%s: target %d does not exist!\n", __func__,
+ ccb->ccb_h.target_id);
+ ccb->ccb_h.status = CAM_SEL_TIMEOUT;
+ xpt_done(ccb);
+ return;
+ }
+
+ /*
+ * If this device has an embedded SMP target, we'll talk to it
+ * directly.
+ * figure out what the expander's address is.
+ */
+ if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0)
+ sasaddr = targ->sasaddr;
+
+ /*
+ * If we don't have a SAS address for the expander yet, try
+ * grabbing it from the page 0x83 information cached in the
+ * transport layer for this target. LSI expanders report the
+ * expander SAS address as the port-associated SAS address in
+ * Inquiry VPD page 0x83. Maxim expanders don't report it in page
+ * 0x83.
+ *
+ * XXX KDM disable this for now, but leave it commented out so that
+ * it is obvious that this is another possible way to get the SAS
+ * address.
+ *
+ * The parent handle method below is a little more reliable, and
+ * the other benefit is that it works for devices other than SES
+ * devices. So you can send a SMP request to a da(4) device and it
+ * will get routed to the expander that device is attached to.
+ * (Assuming the da(4) device doesn't contain an SMP target...)
+ */
+#if 0
+ if (sasaddr == 0)
+ sasaddr = xpt_path_sas_addr(ccb->ccb_h.path);
+#endif
+
+ /*
+ * If we still don't have a SAS address for the expander, look for
+ * the parent device of this device, which is probably the expander.
+ */
+ if (sasaddr == 0) {
+ struct mpssas_target *parent_target;
+
+ if (targ->parent_handle == 0x0) {
+ mps_printf(sc, "%s: handle %d does not have a valid "
+ "parent handle!\n", __func__, targ->handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+ }
+ parent_target = mpssas_find_target(sassc, 0,
+ targ->parent_handle);
+
+ if (parent_target == NULL) {
+ mps_printf(sc, "%s: handle %d does not have a valid "
+ "parent target!\n", __func__, targ->handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+ }
+
+ if ((parent_target->devinfo &
+ MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
+ mps_printf(sc, "%s: handle %d parent %d does not "
+ "have an SMP target!\n", __func__,
+ targ->handle, parent_target->handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+
+ }
+
+ sasaddr = parent_target->sasaddr;
+ }
+
+ if (sasaddr == 0) {
+ mps_printf(sc, "%s: unable to find SAS address for handle %d\n",
+ __func__, targ->handle);
+ ccb->ccb_h.status = CAM_REQ_INVALID;
+ goto bailout;
+ }
+ mpssas_send_smpcmd(sassc, ccb, sasaddr);
+
+ return;
+
+bailout:
+ xpt_done(ccb);
+
+}
+
+#endif /* __FreeBSD_version >= 900026 */
+
static void
mpssas_action_resetdev(struct mpssas_softc *sassc, union ccb *ccb)
{
diff --git a/sys/dev/mps/mps_user.c b/sys/dev/mps/mps_user.c
index ede4c02..7ca90c1 100644
--- a/sys/dev/mps/mps_user.c
+++ b/sys/dev/mps/mps_user.c
@@ -322,7 +322,7 @@ mps_user_write_cfg_page(struct mps_softc *sc,
return (0);
}
-static void
+void
mpi_init_sge(struct mps_command *cm, void *req, void *sge)
{
int off, space;
diff --git a/sys/dev/mps/mpsvar.h b/sys/dev/mps/mpsvar.h
index db91030..f578347 100644
--- a/sys/dev/mps/mpsvar.h
+++ b/sys/dev/mps/mpsvar.h
@@ -60,11 +60,19 @@ struct mps_chain {
uint32_t chain_busaddr;
};
+/*
+ * This needs to be at least 2 to support SMP passthrough.
+ */
+#define MPS_IOVEC_COUNT 2
+
struct mps_command {
TAILQ_ENTRY(mps_command) cm_link;
struct mps_softc *cm_sc;
void *cm_data;
u_int cm_length;
+ struct uio cm_uio;
+ struct iovec cm_iovec[MPS_IOVEC_COUNT];
+ u_int cm_max_segs;
u_int cm_sglsize;
MPI2_SGE_IO_UNION *cm_sge;
uint8_t *cm_req;
@@ -82,6 +90,8 @@ struct mps_command {
#define MPS_CM_FLAGS_DATAIN (1 << 4)
#define MPS_CM_FLAGS_WAKEUP (1 << 5)
#define MPS_CM_FLAGS_ACTIVE (1 << 6)
+#define MPS_CM_FLAGS_USE_UIO (1 << 7)
+#define MPS_CM_FLAGS_SMP_PASS (1 << 8)
u_int cm_state;
#define MPS_CM_STATE_FREE 0
#define MPS_CM_STATE_BUSY 1
@@ -238,12 +248,15 @@ mps_free_command(struct mps_softc *sc, struct mps_command *cm)
{
struct mps_chain *chain, *chain_temp;
- if (cm->cm_reply != NULL)
+ if (cm->cm_reply != NULL) {
mps_free_reply(sc, cm->cm_reply_data);
+ cm->cm_reply = NULL;
+ }
cm->cm_flags = 0;
cm->cm_complete = NULL;
cm->cm_complete_data = NULL;
cm->cm_targ = 0;
+ cm->cm_max_segs = 0;
cm->cm_state = MPS_CM_STATE_FREE;
TAILQ_FOREACH_SAFE(chain, &cm->cm_chain_list, chain_link, chain_temp) {
TAILQ_REMOVE(&cm->cm_chain_list, chain, chain_link);
@@ -368,6 +381,7 @@ int mps_map_command(struct mps_softc *sc, struct mps_command *cm);
int mps_read_config_page(struct mps_softc *, struct mps_config_params *);
int mps_write_config_page(struct mps_softc *, struct mps_config_params *);
void mps_memaddr_cb(void *, bus_dma_segment_t *, int , int );
+void mpi_init_sge(struct mps_command *cm, void *req, void *sge);
int mps_attach_user(struct mps_softc *);
void mps_detach_user(struct mps_softc *);
OpenPOWER on IntegriCloud