summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorattilio <attilio@FreeBSD.org>2010-03-14 22:38:18 +0000
committerattilio <attilio@FreeBSD.org>2010-03-14 22:38:18 +0000
commit500e7d9ffdb336afe9400f12c3e707a7970a2673 (patch)
treefe47fb093b170374bd5d6cee1acefd204c62041a /sys
parent40e45eec2fe36d0d9757030dce0ed34476a0f233 (diff)
downloadFreeBSD-src-500e7d9ffdb336afe9400f12c3e707a7970a2673.zip
FreeBSD-src-500e7d9ffdb336afe9400f12c3e707a7970a2673.tar.gz
Checkin a facility for specifying a passthrough FIB from userland.
arcconf tool by Adaptec already seems to use for identifying the Serial Number of the devices. Some simple things (like FIB setup and bound checks) are retrieved from the Adaptec's driver, but this implementation is quite different because it does use the normal buffer dmat area for loading segments and not a special one (like the Adaptec's one does). Sponsored by: Sandvine Incorporated Discussed with: emaste, scottl Reviewed by: emaste, scottl MFC: 2 weeks
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/aac/aac.c142
1 files changed, 141 insertions, 1 deletions
diff --git a/sys/dev/aac/aac.c b/sys/dev/aac/aac.c
index 363972d..fa25716 100644
--- a/sys/dev/aac/aac.c
+++ b/sys/dev/aac/aac.c
@@ -3063,7 +3063,147 @@ out:
static int
aac_ioctl_send_raw_srb(struct aac_softc *sc, caddr_t arg)
{
- return (EINVAL);
+ struct aac_command *cm;
+ struct aac_event *event;
+ struct aac_fib *fib;
+ struct aac_srb *srbcmd, *user_srb;
+ struct aac_sg_entry *sge;
+ struct aac_sg_entry64 *sge64;
+ void *srb_sg_address, *ureply;
+ uint32_t fibsize, srb_sg_bytecount;
+ int error, transfer_data;
+
+ fwprintf(sc, HBA_FLAGS_DBG_FUNCTION_ENTRY_B, "");
+
+ cm = NULL;
+ transfer_data = 0;
+ fibsize = 0;
+ user_srb = (struct aac_srb *)arg;
+
+ mtx_lock(&sc->aac_io_lock);
+ if (aac_alloc_command(sc, &cm)) {
+ event = malloc(sizeof(struct aac_event), M_AACBUF,
+ M_NOWAIT | M_ZERO);
+ if (event == NULL) {
+ error = EBUSY;
+ mtx_unlock(&sc->aac_io_lock);
+ goto out;
+ }
+ event->ev_type = AAC_EVENT_CMFREE;
+ event->ev_callback = aac_ioctl_event;
+ event->ev_arg = &cm;
+ aac_add_event(sc, event);
+ msleep(cm, &sc->aac_io_lock, 0, "aacraw", 0);
+ }
+ mtx_unlock(&sc->aac_io_lock);
+
+ cm->cm_data = NULL;
+ fib = cm->cm_fib;
+ srbcmd = (struct aac_srb *)fib->data;
+ error = copyin(&user_srb->data_len, &fibsize, sizeof(uint32_t));
+ if (error != 0)
+ goto out;
+ if (fibsize > (sc->aac_max_fib_size - sizeof(struct aac_fib_header))) {
+ error = EINVAL;
+ goto out;
+ }
+ error = copyin(user_srb, srbcmd, fibsize);
+ if (error != 0)
+ goto out;
+ srbcmd->function = 0;
+ srbcmd->retry_limit = 0;
+ if (srbcmd->sg_map.SgCount > 1) {
+ error = EINVAL;
+ goto out;
+ }
+
+ /* Retrieve correct SG entries. */
+ if (fibsize == (sizeof(struct aac_srb) +
+ srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry))) {
+ sge = srbcmd->sg_map.SgEntry;
+ sge64 = NULL;
+ srb_sg_bytecount = sge->SgByteCount;
+#ifdef __amd64__
+ srb_sg_address = (void *)(uint64_t)sge->SgAddress;
+#else
+ srb_sg_address = (void *)sge->SgAddress;
+#endif
+ }
+#ifdef __amd64__
+ else if (fibsize == (sizeof(struct aac_srb) +
+ srbcmd->sg_map.SgCount * sizeof(struct aac_sg_entry64))) {
+ sge = NULL;
+ sge64 = (struct aac_sg_entry64 *)srbcmd->sg_map.SgEntry;
+ srb_sg_bytecount = sge64->SgByteCount;
+ srb_sg_address = (void *)sge64->SgAddress;
+ if (sge64->SgAddress > 0xffffffffull &&
+ (sc->flags & AAC_FLAGS_SG_64BIT) == 0) {
+ error = EINVAL;
+ goto out;
+ }
+ }
+#endif
+ else {
+ error = EINVAL;
+ goto out;
+ }
+ ureply = (char *)arg + fibsize;
+ srbcmd->data_len = srb_sg_bytecount;
+ if (srbcmd->sg_map.SgCount == 1)
+ transfer_data = 1;
+
+ cm->cm_sgtable = (struct aac_sg_table *)&srbcmd->sg_map;
+ if (transfer_data) {
+ cm->cm_datalen = srb_sg_bytecount;
+ cm->cm_data = malloc(cm->cm_datalen, M_AACBUF, M_NOWAIT);
+ if (cm->cm_data == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ if (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN)
+ cm->cm_flags |= AAC_CMD_DATAIN;
+ if (srbcmd->flags & AAC_SRB_FLAGS_DATA_OUT) {
+ cm->cm_flags |= AAC_CMD_DATAOUT;
+ error = copyin(srb_sg_address, cm->cm_data,
+ cm->cm_datalen);
+ if (error != 0)
+ goto out;
+ }
+ }
+
+ fib->Header.Size = sizeof(struct aac_fib_header) +
+ sizeof(struct aac_srb);
+ fib->Header.XferState =
+ AAC_FIBSTATE_HOSTOWNED |
+ AAC_FIBSTATE_INITIALISED |
+ AAC_FIBSTATE_EMPTY |
+ AAC_FIBSTATE_FROMHOST |
+ AAC_FIBSTATE_REXPECTED |
+ AAC_FIBSTATE_NORM |
+ AAC_FIBSTATE_ASYNC |
+ AAC_FIBSTATE_FAST_RESPONSE;
+ fib->Header.Command = (sc->flags & AAC_FLAGS_SG_64BIT) != 0 ?
+ ScsiPortCommandU64 : ScsiPortCommand;
+
+ mtx_lock(&sc->aac_io_lock);
+ aac_wait_command(cm);
+ mtx_unlock(&sc->aac_io_lock);
+
+ if (transfer_data && (srbcmd->flags & AAC_SRB_FLAGS_DATA_IN) != 0) {
+ error = copyout(cm->cm_data, srb_sg_address, cm->cm_datalen);
+ if (error != 0)
+ goto out;
+ }
+ error = copyout(fib->data, ureply, sizeof(struct aac_srb_response));
+out:
+ if (cm != NULL) {
+ if (cm->cm_data != NULL)
+ free(cm->cm_data, M_AACBUF);
+ mtx_lock(&sc->aac_io_lock);
+ aac_release_command(cm);
+ mtx_unlock(&sc->aac_io_lock);
+ }
+ return(error);
}
/*
OpenPOWER on IntegriCloud