freebsd-dev/sys/dev/mpt/mpt_user.c
John Baldwin ee98c4a50e Add a new personality to mpt(4) devices to allow userland applications to
perform various operations on a controller.  Specifically, for each mpt(4)
device, create a character device in devfs which accepts ioctl requests for
reading and writing configuration pages and performing RAID actions.

MFC after:	1 week
Reviewed by:	scottl
2008-05-06 20:49:53 +00:00

756 lines
21 KiB
C

/*-
* Copyright (c) 2008 Yahoo!, Inc.
* All rights reserved.
* Written by: John Baldwin <jhb@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of any co-contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* LSI MPT-Fusion Host Adapter FreeBSD userland interface
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/ioccom.h>
#include <sys/mpt_ioctl.h>
#include <dev/mpt/mpt.h>
struct mpt_user_raid_action_result {
uint32_t volume_status;
uint32_t action_data[4];
uint16_t action_status;
};
static mpt_probe_handler_t mpt_user_probe;
static mpt_attach_handler_t mpt_user_attach;
static mpt_enable_handler_t mpt_user_enable;
static mpt_ready_handler_t mpt_user_ready;
static mpt_event_handler_t mpt_user_event;
static mpt_reset_handler_t mpt_user_reset;
static mpt_detach_handler_t mpt_user_detach;
static struct mpt_personality mpt_user_personality = {
.name = "mpt_user",
.probe = mpt_user_probe,
.attach = mpt_user_attach,
.enable = mpt_user_enable,
.ready = mpt_user_ready,
.event = mpt_user_event,
.reset = mpt_user_reset,
.detach = mpt_user_detach,
};
DECLARE_MPT_PERSONALITY(mpt_user, SI_ORDER_SECOND);
static mpt_reply_handler_t mpt_user_reply_handler;
static d_open_t mpt_open;
static d_close_t mpt_close;
static d_ioctl_t mpt_ioctl;
static struct cdevsw mpt_cdevsw = {
.d_version = D_VERSION,
.d_flags = 0,
.d_open = mpt_open,
.d_close = mpt_close,
.d_ioctl = mpt_ioctl,
.d_name = "mpt",
};
static MALLOC_DEFINE(M_MPTUSER, "mpt_user", "Buffers for mpt(4) ioctls");
static uint32_t user_handler_id = MPT_HANDLER_ID_NONE;
int
mpt_user_probe(struct mpt_softc *mpt)
{
/* Attach to every controller. */
return (0);
}
int
mpt_user_attach(struct mpt_softc *mpt)
{
mpt_handler_t handler;
int error, unit;
MPT_LOCK(mpt);
handler.reply_handler = mpt_user_reply_handler;
error = mpt_register_handler(mpt, MPT_HANDLER_REPLY, handler,
&user_handler_id);
MPT_UNLOCK(mpt);
if (error != 0) {
mpt_prt(mpt, "Unable to register user handler!\n");
return (error);
}
unit = device_get_unit(mpt->dev);
mpt->cdev = make_dev(&mpt_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640,
"mpt%d", unit);
if (mpt->cdev == NULL) {
MPT_LOCK(mpt);
mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler,
user_handler_id);
MPT_UNLOCK(mpt);
return (ENOMEM);
}
mpt->cdev->si_drv1 = mpt;
return (0);
}
int
mpt_user_enable(struct mpt_softc *mpt)
{
return (0);
}
void
mpt_user_ready(struct mpt_softc *mpt)
{
}
int
mpt_user_event(struct mpt_softc *mpt, request_t *req,
MSG_EVENT_NOTIFY_REPLY *msg)
{
/* Someday we may want to let a user daemon listen for events? */
return (0);
}
void
mpt_user_reset(struct mpt_softc *mpt, int type)
{
}
void
mpt_user_detach(struct mpt_softc *mpt)
{
mpt_handler_t handler;
/* XXX: do a purge of pending requests? */
destroy_dev(mpt->cdev);
MPT_LOCK(mpt);
handler.reply_handler = mpt_user_reply_handler;
mpt_deregister_handler(mpt, MPT_HANDLER_REPLY, handler,
user_handler_id);
MPT_UNLOCK(mpt);
}
static int
mpt_open(struct cdev *dev, int flags, int fmt, d_thread_t *td)
{
return (0);
}
static int
mpt_close(struct cdev *dev, int flags, int fmt, d_thread_t *td)
{
return (0);
}
static int
mpt_user_read_cfg_header(struct mpt_softc *mpt,
struct mpt_cfg_page_req *page_req)
{
request_t *req;
cfgparms_t params;
MSG_CONFIG *cfgp;
int error;
req = mpt_get_request(mpt, TRUE);
if (req == NULL) {
mpt_prt(mpt, "mpt_user_read_cfg_header: Get request failed!\n");
return (ENOMEM);
}
params.Action = MPI_CONFIG_ACTION_PAGE_HEADER;
params.PageVersion = 0;
params.PageLength = 0;
params.PageNumber = page_req->header.PageNumber;
params.PageType = page_req->header.PageType;
params.PageAddress = page_req->page_address;
error = mpt_issue_cfg_req(mpt, req, &params, /*addr*/0, /*len*/0,
TRUE, 5000);
if (error != 0) {
/*
* Leave the request. Without resetting the chip, it's
* still owned by it and we'll just get into trouble
* freeing it now. Mark it as abandoned so that if it
* shows up later it can be freed.
*/
mpt_prt(mpt, "read_cfg_header timed out\n");
return (ETIMEDOUT);
}
page_req->ioc_status = req->IOCStatus;
if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
cfgp = req->req_vbuf;
bcopy(&cfgp->Header, &page_req->header,
sizeof(page_req->header));
}
mpt_free_request(mpt, req);
return (0);
}
static int
mpt_user_read_cfg_page(struct mpt_softc *mpt, struct mpt_cfg_page_req *page_req,
void *mpt_page)
{
CONFIG_PAGE_HEADER *hdr;
request_t *req;
cfgparms_t params;
int error;
req = mpt_get_request(mpt, TRUE);
if (req == NULL) {
mpt_prt(mpt, "mpt_user_read_cfg_page: Get request failed!\n");
return (ENOMEM);
}
hdr = mpt_page;
params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
params.PageVersion = hdr->PageVersion;
params.PageLength = hdr->PageLength;
params.PageNumber = hdr->PageNumber;
params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK;
params.PageAddress = page_req->page_address;
error = mpt_issue_cfg_req(mpt, req, &params,
req->req_pbuf + MPT_RQSL(mpt),
page_req->len, TRUE, 5000);
if (error != 0) {
mpt_prt(mpt, "mpt_user_read_cfg_page timed out\n");
return (ETIMEDOUT);
}
page_req->ioc_status = req->IOCStatus;
if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
bus_dmamap_sync(mpt->request_dmat, mpt->request_dmap,
BUS_DMASYNC_POSTREAD);
memcpy(mpt_page, ((uint8_t *)req->req_vbuf)+MPT_RQSL(mpt),
page_req->len);
}
mpt_free_request(mpt, req);
return (0);
}
static int
mpt_user_read_extcfg_header(struct mpt_softc *mpt,
struct mpt_ext_cfg_page_req *ext_page_req)
{
request_t *req;
cfgparms_t params;
MSG_CONFIG_REPLY *cfgp;
int error;
req = mpt_get_request(mpt, TRUE);
if (req == NULL) {
mpt_prt(mpt, "mpt_user_read_extcfg_header: Get request failed!\n");
return (ENOMEM);
}
params.Action = MPI_CONFIG_ACTION_PAGE_HEADER;
params.PageVersion = ext_page_req->header.PageVersion;
params.PageLength = 0;
params.PageNumber = ext_page_req->header.PageNumber;
params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
params.PageAddress = ext_page_req->page_address;
params.ExtPageType = ext_page_req->header.ExtPageType;
params.ExtPageLength = 0;
error = mpt_issue_cfg_req(mpt, req, &params, /*addr*/0, /*len*/0,
TRUE, 5000);
if (error != 0) {
/*
* Leave the request. Without resetting the chip, it's
* still owned by it and we'll just get into trouble
* freeing it now. Mark it as abandoned so that if it
* shows up later it can be freed.
*/
mpt_prt(mpt, "mpt_user_read_extcfg_header timed out\n");
return (ETIMEDOUT);
}
ext_page_req->ioc_status = req->IOCStatus;
if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
cfgp = req->req_vbuf;
ext_page_req->header.PageVersion = cfgp->Header.PageVersion;
ext_page_req->header.PageNumber = cfgp->Header.PageNumber;
ext_page_req->header.PageType = cfgp->Header.PageType;
ext_page_req->header.ExtPageLength = cfgp->ExtPageLength;
ext_page_req->header.ExtPageType = cfgp->ExtPageType;
}
mpt_free_request(mpt, req);
return (0);
}
static int
mpt_user_read_extcfg_page(struct mpt_softc *mpt,
struct mpt_ext_cfg_page_req *ext_page_req, void *mpt_page)
{
CONFIG_EXTENDED_PAGE_HEADER *hdr;
request_t *req;
cfgparms_t params;
int error;
req = mpt_get_request(mpt, TRUE);
if (req == NULL) {
mpt_prt(mpt, "mpt_user_read_extcfg_page: Get request failed!\n");
return (ENOMEM);
}
hdr = mpt_page;
params.Action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
params.PageVersion = hdr->PageVersion;
params.PageLength = 0;
params.PageNumber = hdr->PageNumber;
params.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
params.PageAddress = ext_page_req->page_address;
params.ExtPageType = hdr->ExtPageType;
params.ExtPageLength = hdr->ExtPageLength;
error = mpt_issue_cfg_req(mpt, req, &params,
req->req_pbuf + MPT_RQSL(mpt),
ext_page_req->len, TRUE, 5000);
if (error != 0) {
mpt_prt(mpt, "mpt_user_read_extcfg_page timed out\n");
return (ETIMEDOUT);
}
ext_page_req->ioc_status = req->IOCStatus;
if ((req->IOCStatus & MPI_IOCSTATUS_MASK) == MPI_IOCSTATUS_SUCCESS) {
bus_dmamap_sync(mpt->request_dmat, mpt->request_dmap,
BUS_DMASYNC_POSTREAD);
memcpy(mpt_page, ((uint8_t *)req->req_vbuf)+MPT_RQSL(mpt),
ext_page_req->len);
}
mpt_free_request(mpt, req);
return (0);
}
static int
mpt_user_write_cfg_page(struct mpt_softc *mpt,
struct mpt_cfg_page_req *page_req, void *mpt_page)
{
CONFIG_PAGE_HEADER *hdr;
request_t *req;
cfgparms_t params;
u_int hdr_attr;
int error;
hdr = mpt_page;
hdr_attr = hdr->PageType & MPI_CONFIG_PAGEATTR_MASK;
if (hdr_attr != MPI_CONFIG_PAGEATTR_CHANGEABLE &&
hdr_attr != MPI_CONFIG_PAGEATTR_PERSISTENT) {
mpt_prt(mpt, "page type 0x%x not changeable\n",
hdr->PageType & MPI_CONFIG_PAGETYPE_MASK);
return (EINVAL);
}
#if 0
/*
* We shouldn't mask off other bits here.
*/
hdr->PageType &= ~MPI_CONFIG_PAGETYPE_MASK;
#endif
req = mpt_get_request(mpt, TRUE);
if (req == NULL)
return (ENOMEM);
memcpy(((caddr_t)req->req_vbuf) + MPT_RQSL(mpt), mpt_page,
page_req->len);
/*
* There isn't any point in restoring stripped out attributes
* if you then mask them going down to issue the request.
*/
params.Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
params.PageVersion = hdr->PageVersion;
params.PageLength = hdr->PageLength;
params.PageNumber = hdr->PageNumber;
params.PageAddress = page_req->page_address;
#if 0
/* Restore stripped out attributes */
hdr->PageType |= hdr_attr;
params.PageType = hdr->PageType & MPI_CONFIG_PAGETYPE_MASK;
#else
params.PageType = hdr->PageType;
#endif
error = mpt_issue_cfg_req(mpt, req, &params,
req->req_pbuf + MPT_RQSL(mpt),
page_req->len, TRUE, 5000);
if (error != 0) {
mpt_prt(mpt, "mpt_write_cfg_page timed out\n");
return (ETIMEDOUT);
}
page_req->ioc_status = req->IOCStatus;
mpt_free_request(mpt, req);
return (0);
}
static int
mpt_user_reply_handler(struct mpt_softc *mpt, request_t *req,
uint32_t reply_desc, MSG_DEFAULT_REPLY *reply_frame)
{
MSG_RAID_ACTION_REPLY *reply;
struct mpt_user_raid_action_result *res;
if (req == NULL)
return (TRUE);
if (reply_frame != NULL) {
bus_dmamap_sync(mpt->request_dmat, mpt->request_dmap,
BUS_DMASYNC_POSTREAD);
reply = (MSG_RAID_ACTION_REPLY *)reply_frame;
req->IOCStatus = le16toh(reply->IOCStatus);
res = (struct mpt_user_raid_action_result *)
(((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt));
res->action_status = reply->ActionStatus;
res->volume_status = reply->VolumeStatus;
bcopy(&reply->ActionData, res->action_data,
sizeof(res->action_data));
}
req->state &= ~REQ_STATE_QUEUED;
req->state |= REQ_STATE_DONE;
TAILQ_REMOVE(&mpt->request_pending_list, req, links);
if ((req->state & REQ_STATE_NEED_WAKEUP) != 0) {
wakeup(req);
} else if ((req->state & REQ_STATE_TIMEDOUT) != 0) {
/*
* Whew- we can free this request (late completion)
*/
mpt_free_request(mpt, req);
}
return (TRUE);
}
/*
* We use the first part of the request buffer after the request frame
* to hold the action data and action status from the RAID reply. The
* rest of the request buffer is used to hold the buffer for the
* action SGE.
*/
static int
mpt_user_raid_action(struct mpt_softc *mpt, struct mpt_raid_action *raid_act,
void *buf)
{
request_t *req;
struct mpt_user_raid_action_result *res;
MSG_RAID_ACTION_REQUEST *rap;
SGE_SIMPLE32 *se;
int error;
req = mpt_get_request(mpt, TRUE);
if (req == NULL)
return (ENOMEM);
rap = req->req_vbuf;
memset(rap, 0, sizeof *rap);
rap->Action = raid_act->action;
rap->ActionDataWord = raid_act->action_data_word;
rap->Function = MPI_FUNCTION_RAID_ACTION;
rap->VolumeID = raid_act->volume_id;
rap->VolumeBus = raid_act->volume_bus;
rap->PhysDiskNum = raid_act->phys_disk_num;
se = (SGE_SIMPLE32 *)&rap->ActionDataSGE;
if (buf != 0 && raid_act->len != 0) {
memcpy(((caddr_t)req->req_vbuf) + MPT_RQSL(mpt) +
sizeof(struct mpt_user_raid_action_result), buf,
raid_act->len);
se->Address = req->req_pbuf + MPT_RQSL(mpt) +
sizeof(struct mpt_user_raid_action_result);
MPI_pSGE_SET_LENGTH(se, raid_act->len);
MPI_pSGE_SET_FLAGS(se, (MPI_SGE_FLAGS_SIMPLE_ELEMENT |
MPI_SGE_FLAGS_LAST_ELEMENT | MPI_SGE_FLAGS_END_OF_BUFFER |
MPI_SGE_FLAGS_END_OF_LIST |
raid_act->write ? MPI_SGE_FLAGS_HOST_TO_IOC :
MPI_SGE_FLAGS_IOC_TO_HOST));
}
rap->MsgContext = htole32(req->index | user_handler_id);
mpt_check_doorbell(mpt);
mpt_send_cmd(mpt, req);
error = mpt_wait_req(mpt, req, REQ_STATE_DONE, REQ_STATE_DONE, TRUE,
2000);
if (error != 0) {
/*
* Leave request so it can be cleaned up later.
*/
mpt_prt(mpt, "mpt_user_raid_action timed out\n");
return (error);
}
raid_act->ioc_status = req->IOCStatus;
if ((req->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) {
mpt_free_request(mpt, req);
return (0);
}
res = (struct mpt_user_raid_action_result *)
(((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt));
raid_act->volume_status = res->volume_status;
raid_act->action_status = res->action_status;
bcopy(res->action_data, raid_act->action_data,
sizeof(res->action_data));
if (buf != NULL)
memcpy(buf, ((uint8_t *)req->req_vbuf) + MPT_RQSL(mpt) +
sizeof(struct mpt_user_raid_action_result), raid_act->len);
mpt_free_request(mpt, req);
return (0);
}
#ifdef __amd64__
#define PTRIN(p) ((void *)(uintptr_t)(p))
#define PTROUT(v) ((u_int32_t)(uintptr_t)(v))
#endif
static int
mpt_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, d_thread_t *td)
{
struct mpt_softc *mpt;
struct mpt_cfg_page_req *page_req;
struct mpt_ext_cfg_page_req *ext_page_req;
struct mpt_raid_action *raid_act;
#ifdef __amd64__
struct mpt_cfg_page_req32 *page_req32;
struct mpt_cfg_page_req page_req_swab;
struct mpt_ext_cfg_page_req32 *ext_page_req32;
struct mpt_ext_cfg_page_req ext_page_req_swab;
struct mpt_raid_action32 *raid_act32;
struct mpt_raid_action raid_act_swab;
#endif
void *mpt_page;
int error;
mpt = dev->si_drv1;
page_req = (void *)arg;
ext_page_req = (void *)arg;
raid_act = (void *)arg;
mpt_page = NULL;
#ifdef __amd64__
/* Convert 32-bit structs to native ones. */
page_req32 = (void *)arg;
ext_page_req32 = (void *)arg;
raid_act32 = (void *)arg;
switch (cmd) {
case MPTIO_READ_CFG_HEADER32:
case MPTIO_READ_CFG_PAGE32:
case MPTIO_WRITE_CFG_PAGE32:
page_req = &page_req_swab;
page_req->header = page_req32->header;
page_req->page_address = page_req32->page_address;
page_req->buf = PTRIN(page_req32->buf);
page_req->len = page_req32->len;
page_req->ioc_status = page_req32->ioc_status;
break;
case MPTIO_READ_EXT_CFG_HEADER32:
case MPTIO_READ_EXT_CFG_PAGE32:
ext_page_req = &ext_page_req_swab;
ext_page_req->header = ext_page_req32->header;
ext_page_req->page_address = ext_page_req32->page_address;
ext_page_req->buf = PTRIN(ext_page_req32->buf);
ext_page_req->len = ext_page_req32->len;
ext_page_req->ioc_status = ext_page_req32->ioc_status;
break;
case MPTIO_RAID_ACTION32:
raid_act = &raid_act_swab;
raid_act->action = raid_act32->action;
raid_act->volume_bus = raid_act32->volume_bus;
raid_act->volume_id = raid_act32->volume_id;
raid_act->phys_disk_num = raid_act32->phys_disk_num;
raid_act->action_data_word = raid_act32->action_data_word;
raid_act->buf = PTRIN(raid_act32->buf);
raid_act->len = raid_act32->len;
raid_act->volume_status = raid_act32->volume_status;
bcopy(raid_act32->action_data, raid_act->action_data,
sizeof(raid_act->action_data));
raid_act->action_status = raid_act32->action_status;
raid_act->ioc_status = raid_act32->ioc_status;
raid_act->write = raid_act32->write;
break;
}
#endif
switch (cmd) {
#ifdef __amd64__
case MPTIO_READ_CFG_HEADER32:
#endif
case MPTIO_READ_CFG_HEADER:
MPT_LOCK(mpt);
error = mpt_user_read_cfg_header(mpt, page_req);
MPT_UNLOCK(mpt);
break;
#ifdef __amd64__
case MPTIO_READ_CFG_PAGE32:
#endif
case MPTIO_READ_CFG_PAGE:
if (page_req->len > (MPT_REQUEST_AREA - MPT_RQSL(mpt))) {
error = EINVAL;
break;
}
mpt_page = malloc(page_req->len, M_MPTUSER, M_WAITOK);
error = copyin(page_req->buf, mpt_page,
sizeof(CONFIG_PAGE_HEADER));
if (error)
break;
MPT_LOCK(mpt);
error = mpt_user_read_cfg_page(mpt, page_req, mpt_page);
MPT_UNLOCK(mpt);
if (error)
break;
error = copyout(mpt_page, page_req->buf, page_req->len);
break;
#ifdef __amd64__
case MPTIO_READ_EXT_CFG_HEADER32:
#endif
case MPTIO_READ_EXT_CFG_HEADER:
MPT_LOCK(mpt);
error = mpt_user_read_extcfg_header(mpt, ext_page_req);
MPT_UNLOCK(mpt);
break;
#ifdef __amd64__
case MPTIO_READ_EXT_CFG_PAGE32:
#endif
case MPTIO_READ_EXT_CFG_PAGE:
if (ext_page_req->len > (MPT_REQUEST_AREA - MPT_RQSL(mpt))) {
error = EINVAL;
break;
}
mpt_page = malloc(ext_page_req->len, M_MPTUSER, M_WAITOK);
error = copyin(ext_page_req->buf, mpt_page,
sizeof(CONFIG_EXTENDED_PAGE_HEADER));
if (error)
break;
MPT_LOCK(mpt);
error = mpt_user_read_extcfg_page(mpt, ext_page_req, mpt_page);
MPT_UNLOCK(mpt);
if (error)
break;
error = copyout(mpt_page, ext_page_req->buf, ext_page_req->len);
break;
#ifdef __amd64__
case MPTIO_WRITE_CFG_PAGE32:
#endif
case MPTIO_WRITE_CFG_PAGE:
if (page_req->len > (MPT_REQUEST_AREA - MPT_RQSL(mpt))) {
error = EINVAL;
break;
}
mpt_page = malloc(page_req->len, M_MPTUSER, M_WAITOK);
error = copyin(page_req->buf, mpt_page, page_req->len);
if (error)
break;
MPT_LOCK(mpt);
error = mpt_user_write_cfg_page(mpt, page_req, mpt_page);
MPT_UNLOCK(mpt);
break;
#ifdef __amd64__
case MPTIO_RAID_ACTION32:
#endif
case MPTIO_RAID_ACTION:
if (raid_act->buf != NULL) {
if (raid_act->len >
(MPT_REQUEST_AREA - MPT_RQSL(mpt) -
sizeof(struct mpt_user_raid_action_result))) {
error = EINVAL;
break;
}
mpt_page = malloc(raid_act->len, M_MPTUSER, M_WAITOK);
error = copyin(raid_act->buf, mpt_page, raid_act->len);
if (error)
break;
}
MPT_LOCK(mpt);
error = mpt_user_raid_action(mpt, raid_act, mpt_page);
MPT_UNLOCK(mpt);
if (error)
break;
error = copyout(mpt_page, raid_act->buf, raid_act->len);
break;
default:
error = ENOIOCTL;
break;
}
if (mpt_page != NULL)
free(mpt_page, M_MPTUSER);
if (error)
return (error);
#ifdef __amd64__
/* Convert native structs to 32-bit ones. */
switch (cmd) {
case MPTIO_READ_CFG_HEADER32:
case MPTIO_READ_CFG_PAGE32:
case MPTIO_WRITE_CFG_PAGE32:
page_req32->header = page_req->header;
page_req32->page_address = page_req->page_address;
page_req32->buf = PTROUT(page_req->buf);
page_req32->len = page_req->len;
page_req32->ioc_status = page_req->ioc_status;
break;
case MPTIO_READ_EXT_CFG_HEADER32:
case MPTIO_READ_EXT_CFG_PAGE32:
ext_page_req32->header = ext_page_req->header;
ext_page_req32->page_address = ext_page_req->page_address;
ext_page_req32->buf = PTROUT(ext_page_req->buf);
ext_page_req32->len = ext_page_req->len;
ext_page_req32->ioc_status = ext_page_req->ioc_status;
break;
case MPTIO_RAID_ACTION32:
raid_act32->action = raid_act->action;
raid_act32->volume_bus = raid_act->volume_bus;
raid_act32->volume_id = raid_act->volume_id;
raid_act32->phys_disk_num = raid_act->phys_disk_num;
raid_act32->action_data_word = raid_act->action_data_word;
raid_act32->buf = PTROUT(raid_act->buf);
raid_act32->len = raid_act->len;
raid_act32->volume_status = raid_act->volume_status;
bcopy(raid_act->action_data, raid_act32->action_data,
sizeof(raid_act->action_data));
raid_act32->action_status = raid_act->action_status;
raid_act32->ioc_status = raid_act->ioc_status;
raid_act32->write = raid_act->write;
break;
}
#endif
return (0);
}