aac_ioctl.c revision 0749e8de8370b977962d1cbaa31a8ebbaf755a01
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2005-06 Adaptec, Inc.
* Copyright (c) 2005-06 Adaptec Inc., Achim Leubner
* Copyright (c) 2000 Michael Smith
* Copyright (c) 2001 Scott Long
* Copyright (c) 2000 BSDi
* All rights reserved.
*
* 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.
*
* 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.
*/
#include <sys/modctl.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/ddi.h>
#include <sys/devops.h>
#include <sys/pci.h>
#include <sys/types.h>
#include <sys/ddidmareq.h>
#include <sys/scsi/scsi.h>
#include <sys/ksynch.h>
#include <sys/sunddi.h>
#include <sys/byteorder.h>
#include <sys/kmem.h>
#include "aac_regs.h"
#include "aac.h"
#include "aac_ioctl.h"
struct aac_umem_sge {
uint32_t bcount;
caddr_t addr;
struct aac_cmd acp;
};
/*
* External functions
*/
extern int aac_sync_mbcommand(struct aac_softstate *, uint32_t, uint32_t,
uint32_t, uint32_t, uint32_t, uint32_t *);
extern int aac_cmd_dma_alloc(struct aac_softstate *, struct aac_cmd *,
struct buf *, int, int (*)(), caddr_t);
extern void aac_free_dmamap(struct aac_cmd *);
extern int aac_do_io(struct aac_softstate *, struct aac_cmd *);
extern void aac_cmd_fib_copy(struct aac_softstate *, struct aac_cmd *);
extern void aac_ioctl_complete(struct aac_softstate *, struct aac_cmd *);
extern int aac_return_aif_wait(struct aac_softstate *, struct aac_fib_context *,
struct aac_fib **);
extern int aac_return_aif(struct aac_softstate *, struct aac_fib_context *,
struct aac_fib **);
extern ddi_device_acc_attr_t aac_acc_attr;
extern int aac_check_dma_handle(ddi_dma_handle_t);
/*
* IOCTL command handling functions
*/
static int aac_check_revision(struct aac_softstate *, intptr_t, int);
static int aac_ioctl_send_fib(struct aac_softstate *, intptr_t, int);
static int aac_open_getadapter_fib(struct aac_softstate *, intptr_t, int);
static int aac_next_getadapter_fib(struct aac_softstate *, intptr_t, int);
static int aac_close_getadapter_fib(struct aac_softstate *, intptr_t);
static int aac_send_raw_srb(struct aac_softstate *, dev_t, intptr_t, int);
static int aac_get_pci_info(struct aac_softstate *, intptr_t, int);
static int aac_query_disk(struct aac_softstate *, intptr_t, int);
static int aac_delete_disk(struct aac_softstate *, intptr_t, int);
static int aac_supported_features(struct aac_softstate *, intptr_t, int);
/*
* Warlock directives
*/
_NOTE(SCHEME_PROTECTS_DATA("unique to each handling function", aac_features
aac_pci_info aac_query_disk aac_revision aac_umem_sge))
int
aac_do_ioctl(struct aac_softstate *softs, dev_t dev, int cmd, intptr_t arg,
int mode)
{
int status;
switch (cmd) {
case FSACTL_MINIPORT_REV_CHECK:
AACDB_PRINT_IOCTL(softs, "FSACTL_MINIPORT_REV_CHECK");
status = aac_check_revision(softs, arg, mode);
break;
case FSACTL_SENDFIB:
AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
goto send_fib;
case FSACTL_SEND_LARGE_FIB:
AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_LARGE_FIB");
send_fib:
status = aac_ioctl_send_fib(softs, arg, mode);
break;
case FSACTL_OPEN_GET_ADAPTER_FIB:
AACDB_PRINT_IOCTL(softs, "FSACTL_OPEN_GET_ADAPTER_FIB");
status = aac_open_getadapter_fib(softs, arg, mode);
break;
case FSACTL_GET_NEXT_ADAPTER_FIB:
AACDB_PRINT_IOCTL(softs, "FSACTL_GET_NEXT_ADAPTER_FIB");
status = aac_next_getadapter_fib(softs, arg, mode);
break;
case FSACTL_CLOSE_GET_ADAPTER_FIB:
AACDB_PRINT_IOCTL(softs, "FSACTL_CLOSE_GET_ADAPTER_FIB");
status = aac_close_getadapter_fib(softs, arg);
break;
case FSACTL_SEND_RAW_SRB:
AACDB_PRINT_IOCTL(softs, "FSACTL_SEND_RAW_SRB");
status = aac_send_raw_srb(softs, dev, arg, mode);
break;
case FSACTL_GET_PCI_INFO:
AACDB_PRINT_IOCTL(softs, "FSACTL_GET_PCI_INFO");
status = aac_get_pci_info(softs, arg, mode);
break;
case FSACTL_QUERY_DISK:
AACDB_PRINT_IOCTL(softs, "FSACTL_QUERY_DISK");
status = aac_query_disk(softs, arg, mode);
break;
case FSACTL_DELETE_DISK:
AACDB_PRINT_IOCTL(softs, "FSACTL_DELETE_DISK");
status = aac_delete_disk(softs, arg, mode);
break;
case FSACTL_GET_FEATURES:
AACDB_PRINT_IOCTL(softs, "FSACTL_GET_FEATURES");
status = aac_supported_features(softs, arg, mode);
break;
default:
status = ENOTTY;
AACDB_PRINT(softs, CE_WARN,
"!IOCTL cmd 0x%x not supported", cmd);
break;
}
return (status);
}
/*ARGSUSED*/
static int
aac_check_revision(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_revision_align un;
struct aac_revision *aac_rev = &un.d;
DBCALLED(softs, 2);
/* Copyin the revision struct from userspace */
if (ddi_copyin((void *)arg, aac_rev,
sizeof (struct aac_revision), mode) != 0)
return (EFAULT);
/* Doctor up the response struct */
aac_rev->compat = 1;
aac_rev->version =
((uint32_t)AAC_DRIVER_MAJOR_VERSION << 24) |
((uint32_t)AAC_DRIVER_MINOR_VERSION << 16) |
((uint32_t)AAC_DRIVER_TYPE << 8) |
((uint32_t)AAC_DRIVER_BUGFIX_LEVEL);
aac_rev->build = (uint32_t)AAC_DRIVER_BUILD;
if (ddi_copyout(aac_rev, (void *)arg,
sizeof (struct aac_revision), mode) != 0)
return (EFAULT);
return (0);
}
static int
aac_send_fib(struct aac_softstate *softs, struct aac_cmd *acp)
{
int rval;
acp->flags |= AAC_CMD_NO_CB | AAC_CMD_SYNC;
acp->ac_comp = aac_ioctl_complete;
mutex_enter(&softs->io_lock);
if (softs->state & AAC_STATE_DEAD) {
mutex_exit(&softs->io_lock);
return (ENXIO);
}
rval = aac_do_io(softs, acp);
if (rval == TRAN_ACCEPT) {
rval = 0;
} else if (rval == TRAN_BADPKT) {
AACDB_PRINT(softs, CE_CONT, "User SendFib failed ENXIO");
rval = ENXIO;
} else if (rval == TRAN_BUSY) {
AACDB_PRINT(softs, CE_CONT, "User SendFib failed EBUSY");
rval = EBUSY;
}
mutex_exit(&softs->io_lock);
return (rval);
}
static int
aac_ioctl_send_fib(struct aac_softstate *softs, intptr_t arg, int mode)
{
int hbalen;
struct aac_cmd *acp;
struct aac_fib *fibp;
uint16_t fib_command;
uint32_t fib_xfer_state;
uint16_t fib_data_size, fib_size;
uint16_t fib_sender_size;
int rval;
DBCALLED(softs, 2);
/* Copy in FIB header */
hbalen = sizeof (struct aac_cmd) + softs->aac_max_fib_size;
if ((acp = kmem_zalloc(hbalen, KM_NOSLEEP)) == NULL)
return (ENOMEM);
fibp = (struct aac_fib *)(acp + 1);
acp->fibp = fibp;
if (ddi_copyin((void *)arg, fibp,
sizeof (struct aac_fib_header), mode) != 0) {
rval = EFAULT;
goto finish;
}
fib_xfer_state = LE_32(fibp->Header.XferState);
fib_command = LE_16(fibp->Header.Command);
fib_data_size = LE_16(fibp->Header.Size);
fib_sender_size = LE_16(fibp->Header.SenderSize);
fib_size = fib_data_size + sizeof (struct aac_fib_header);
if (fib_size < fib_sender_size)
fib_size = fib_sender_size;
if (fib_size > softs->aac_max_fib_size) {
rval = EFAULT;
goto finish;
}
/* Copy in FIB data */
if (ddi_copyin(((struct aac_fib *)arg)->data, fibp->data,
fib_data_size, mode) != 0) {
rval = EFAULT;
goto finish;
}
acp->fib_size = fib_size;
fibp->Header.Size = LE_16(fib_size);
/* Process FIB */
if (fib_command == TakeABreakPt) {
#ifdef DEBUG
if (aac_dbflag_on(softs, AACDB_FLAGS_FIB) &&
(softs->debug_fib_flags & AACDB_FLAGS_FIB_IOCTL))
aac_printf(softs, CE_NOTE, "FIB> TakeABreakPt, sz=%d",
fib_size);
#endif
(void) aac_sync_mbcommand(softs, AAC_BREAKPOINT_REQ,
0, 0, 0, 0, NULL);
fibp->Header.XferState = LE_32(0);
} else {
ASSERT(!(fib_xfer_state & AAC_FIBSTATE_ASYNC));
fibp->Header.XferState = LE_32(fib_xfer_state | \
(AAC_FIBSTATE_FROMHOST | AAC_FIBSTATE_REXPECTED));
acp->timeout = AAC_IOCTL_TIMEOUT;
acp->aac_cmd_fib = aac_cmd_fib_copy;
#ifdef DEBUG
acp->fib_flags = AACDB_FLAGS_FIB_IOCTL;
#endif
if ((rval = aac_send_fib(softs, acp)) != 0)
goto finish;
}
if (acp->flags & AAC_CMD_ERR) {
AACDB_PRINT(softs, CE_CONT, "FIB data corrupt");
rval = EIO;
goto finish;
}
if (ddi_copyout(fibp, (void *)arg, acp->fib_size, mode) != 0) {
AACDB_PRINT(softs, CE_CONT, "FIB copyout failed");
rval = EFAULT;
goto finish;
}
rval = 0;
finish:
kmem_free(acp, hbalen);
return (rval);
}
static int
aac_open_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
{
struct aac_fib_context *fibctx_p, *ctx_p;
DBCALLED(softs, 2);
fibctx_p = kmem_zalloc(sizeof (struct aac_fib_context), KM_NOSLEEP);
if (fibctx_p == NULL)
return (ENOMEM);
mutex_enter(&softs->aifq_mutex);
/* All elements are already 0, add to queue */
if (softs->fibctx_p == NULL) {
softs->fibctx_p = fibctx_p;
} else {
for (ctx_p = softs->fibctx_p; ctx_p->next; ctx_p = ctx_p->next)
;
ctx_p->next = fibctx_p;
fibctx_p->prev = ctx_p;
}
/* Evaluate unique value */
fibctx_p->unique = (unsigned long)fibctx_p & 0xfffffffful;
ctx_p = softs->fibctx_p;
while (ctx_p != fibctx_p) {
if (ctx_p->unique == fibctx_p->unique) {
fibctx_p->unique++;
ctx_p = softs->fibctx_p;
} else {
ctx_p = ctx_p->next;
}
}
/* Set ctx_idx to the oldest AIF */
if (softs->aifq_wrap) {
fibctx_p->ctx_idx = softs->aifq_idx;
fibctx_p->ctx_filled = 1;
}
mutex_exit(&softs->aifq_mutex);
if (ddi_copyout(&fibctx_p->unique, (void *)arg,
sizeof (uint32_t), mode) != 0)
return (EFAULT);
return (0);
}
static int
aac_next_getadapter_fib(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_get_adapter_fib_align un;
struct aac_get_adapter_fib *af = &un.d;
struct aac_fib_context *ctx_p;
struct aac_fib *fibp;
int rval;
DBCALLED(softs, 2);
if (ddi_copyin((void *)arg, af, sizeof (*af), mode) != 0)
return (EFAULT);
mutex_enter(&softs->aifq_mutex);
for (ctx_p = softs->fibctx_p; ctx_p; ctx_p = ctx_p->next) {
if (af->context == ctx_p->unique)
break;
}
mutex_exit(&softs->aifq_mutex);
if (ctx_p) {
if (af->wait)
rval = aac_return_aif_wait(softs, ctx_p, &fibp);
else
rval = aac_return_aif(softs, ctx_p, &fibp);
}
else
rval = EFAULT;
finish:
if (rval == 0) {
if (ddi_copyout(fibp,
#ifdef _LP64
(void *)(uint64_t)af->aif_fib,
#else
(void *)af->aif_fib,
#endif
sizeof (struct aac_fib), mode) != 0)
rval = EFAULT;
}
return (rval);
}
static int
aac_close_getadapter_fib(struct aac_softstate *softs, intptr_t arg)
{
struct aac_fib_context *ctx_p;
DBCALLED(softs, 2);
mutex_enter(&softs->aifq_mutex);
for (ctx_p = softs->fibctx_p; ctx_p; ctx_p = ctx_p->next) {
if (ctx_p->unique != (uint32_t)arg)
continue;
if (ctx_p == softs->fibctx_p)
softs->fibctx_p = ctx_p->next;
else
ctx_p->prev->next = ctx_p->next;
if (ctx_p->next)
ctx_p->next->prev = ctx_p->prev;
break;
}
mutex_exit(&softs->aifq_mutex);
if (ctx_p)
kmem_free(ctx_p, sizeof (struct aac_fib_context));
return (0);
}
/*
* The following function comes from Adaptec:
*
* SRB is required for the new management tools
* Note: SRB passed down from IOCTL is always in CPU endianness.
*/
static int
aac_send_raw_srb(struct aac_softstate *softs, dev_t dev, intptr_t arg, int mode)
{
struct aac_cmd *acp;
struct aac_fib *fibp;
struct aac_srb *srb;
uint32_t usr_fib_size;
uint32_t srb_sgcount;
struct aac_umem_sge *usgt = NULL;
struct aac_umem_sge *usge;
ddi_umem_cookie_t cookie;
int umem_flags = 0;
int direct = 0;
int locked = 0;
caddr_t addrlo = (caddr_t)-1;
caddr_t addrhi = 0;
struct aac_sge *sge, *sge0;
int sg64;
int rval;
DBCALLED(softs, 2);
/* Read srb size */
if (ddi_copyin(&((struct aac_srb *)arg)->count, &usr_fib_size,
sizeof (uint32_t), mode) != 0)
return (EFAULT);
if (usr_fib_size > (softs->aac_max_fib_size - \
sizeof (struct aac_fib_header)))
return (EINVAL);
if ((acp = kmem_zalloc(sizeof (struct aac_cmd) + usr_fib_size + \
sizeof (struct aac_fib_header), KM_NOSLEEP)) == NULL)
return (ENOMEM);
acp->fibp = (struct aac_fib *)(acp + 1);
fibp = acp->fibp;
srb = (struct aac_srb *)fibp->data;
/* Copy in srb */
if (ddi_copyin((void *)arg, srb, usr_fib_size, mode) != 0) {
rval = EFAULT;
goto finish;
}
srb_sgcount = srb->sg.SgCount; /* No endianness conversion needed */
if (srb_sgcount == 0)
goto send_fib;
/* Check FIB size */
if (usr_fib_size == (sizeof (struct aac_srb) + \
srb_sgcount * sizeof (struct aac_sg_entry64) - \
sizeof (struct aac_sg_entry))) {
sg64 = 1;
} else if (usr_fib_size == (sizeof (struct aac_srb) + \
(srb_sgcount - 1) * sizeof (struct aac_sg_entry))) {
sg64 = 0;
} else {
rval = EINVAL;
goto finish;
}
/* Read user SG table */
if ((usgt = kmem_zalloc(sizeof (struct aac_umem_sge) * srb_sgcount,
KM_NOSLEEP)) == NULL) {
rval = ENOMEM;
goto finish;
}
for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
if (sg64) {
struct aac_sg_entry64 *sg64p =
(struct aac_sg_entry64 *)srb->sg.SgEntry;
usge->bcount = sg64p->SgByteCount;
usge->addr = (caddr_t)
#ifndef _LP64
(uint32_t)
#endif
sg64p->SgAddress;
} else {
struct aac_sg_entry *sgp = srb->sg.SgEntry;
usge->bcount = sgp->SgByteCount;
usge->addr = (caddr_t)
#ifdef _LP64
(uint64_t)
#endif
sgp->SgAddress;
}
acp->bcount += usge->bcount;
if (usge->addr < addrlo)
addrlo = usge->addr;
if ((usge->addr + usge->bcount) > addrhi)
addrhi = usge->addr + usge->bcount;
}
if (acp->bcount > softs->buf_dma_attr.dma_attr_maxxfer) {
AACDB_PRINT(softs, CE_NOTE,
"large srb xfer size received %d\n", acp->bcount);
rval = EINVAL;
goto finish;
}
/* Lock user buffers */
if (srb->flags & SRB_DataIn) {
umem_flags |= DDI_UMEMLOCK_READ;
direct |= B_READ;
}
if (srb->flags & SRB_DataOut) {
umem_flags |= DDI_UMEMLOCK_WRITE;
direct |= B_WRITE;
}
addrlo = (caddr_t)((uintptr_t)addrlo & (uintptr_t)PAGEMASK);
rval = ddi_umem_lock(addrlo, (((size_t)addrhi + PAGEOFFSET) & \
PAGEMASK) - (size_t)addrlo, umem_flags, &cookie);
if (rval != 0) {
AACDB_PRINT(softs, CE_NOTE, "ddi_umem_lock failed: %d",
rval);
goto finish;
}
locked = 1;
/* Allocate DMA for user buffers */
for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
struct buf *bp;
bp = ddi_umem_iosetup(cookie, (uintptr_t)usge->addr - \
(uintptr_t)addrlo, usge->bcount, direct, dev, 0, NULL,
DDI_UMEM_NOSLEEP);
if (bp == NULL) {
AACDB_PRINT(softs, CE_NOTE, "ddi_umem_iosetup failed");
rval = ENOMEM;
goto finish;
}
if (aac_cmd_dma_alloc(softs, &usge->acp, bp, 0, NULL_FUNC,
0) != AACOK) {
rval = EFAULT;
goto finish;
}
acp->left_cookien += usge->acp.left_cookien;
if (acp->left_cookien > softs->aac_sg_tablesize) {
AACDB_PRINT(softs, CE_NOTE, "large cookiec received %d",
acp->left_cookien);
rval = EINVAL;
goto finish;
}
}
/* Construct aac cmd SG table */
if ((sge = kmem_zalloc(sizeof (struct aac_sge) * acp->left_cookien,
KM_NOSLEEP)) == NULL) {
rval = ENOMEM;
goto finish;
}
acp->sgt = sge;
for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
for (sge0 = usge->acp.sgt;
sge0 < &usge->acp.sgt[usge->acp.left_cookien];
sge0++, sge++)
*sge = *sge0;
}
send_fib:
acp->cmdlen = srb->cdb_size;
acp->timeout = srb->timeout;
/* Send FIB command */
acp->aac_cmd_fib = softs->aac_cmd_fib_scsi;
#ifdef DEBUG
acp->fib_flags = AACDB_FLAGS_FIB_SRB;
#endif
if ((rval = aac_send_fib(softs, acp)) != 0)
goto finish;
/* Status struct */
if (ddi_copyout((struct aac_srb_reply *)fibp->data,
((uint8_t *)arg + usr_fib_size),
sizeof (struct aac_srb_reply), mode) != 0) {
rval = EFAULT;
goto finish;
}
rval = 0;
finish:
if (acp->sgt)
kmem_free(acp->sgt, sizeof (struct aac_sge) * \
acp->left_cookien);
if (usgt) {
for (usge = usgt; usge < &usgt[srb_sgcount]; usge++) {
if (usge->acp.sgt)
kmem_free(usge->acp.sgt,
sizeof (struct aac_sge) * \
usge->acp.left_cookien);
aac_free_dmamap(&usge->acp);
if (usge->acp.bp)
freerbuf(usge->acp.bp);
}
kmem_free(usgt, sizeof (struct aac_umem_sge) * srb_sgcount);
}
if (locked)
ddi_umem_unlock(cookie);
kmem_free(acp, sizeof (struct aac_cmd) + usr_fib_size + \
sizeof (struct aac_fib_header));
return (rval);
}
/*ARGSUSED*/
static int
aac_get_pci_info(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_pci_info_align un;
struct aac_pci_info *resp = &un.d;
pci_regspec_t *pci_rp;
uint_t num;
DBCALLED(softs, 2);
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, softs->devinfo_p,
DDI_PROP_DONTPASS, "reg", (int **)&pci_rp, &num) !=
DDI_PROP_SUCCESS)
return (EINVAL);
if (num < (sizeof (pci_regspec_t) / sizeof (int))) {
ddi_prop_free(pci_rp);
return (EINVAL);
}
resp->bus = PCI_REG_BUS_G(pci_rp->pci_phys_hi);
resp->slot = PCI_REG_DEV_G(pci_rp->pci_phys_hi);
ddi_prop_free(pci_rp);
if (ddi_copyout(resp, (void *)arg,
sizeof (struct aac_pci_info), mode) != 0)
return (EFAULT);
return (0);
}
static int
aac_query_disk(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_query_disk_align un;
struct aac_query_disk *qdisk = &un.d;
struct aac_container *dvp;
DBCALLED(softs, 2);
if (ddi_copyin((void *)arg, qdisk, sizeof (*qdisk), mode) != 0)
return (EFAULT);
if (qdisk->container_no == -1) {
qdisk->container_no = qdisk->target * 16 + qdisk->lun;
} else if (qdisk->bus == -1 && qdisk->target == -1 &&
qdisk->lun == -1) {
if (qdisk->container_no >= AAC_MAX_CONTAINERS)
return (EINVAL);
qdisk->bus = 0;
qdisk->target = (qdisk->container_no & 0xf);
qdisk->lun = (qdisk->container_no >> 4);
} else {
return (EINVAL);
}
mutex_enter(&softs->io_lock);
dvp = &softs->containers[qdisk->container_no];
qdisk->valid = AAC_DEV_IS_VALID(&dvp->dev);
qdisk->locked = dvp->locked;
qdisk->deleted = dvp->deleted;
mutex_exit(&softs->io_lock);
if (ddi_copyout(qdisk, (void *)arg, sizeof (*qdisk), mode) != 0)
return (EFAULT);
return (0);
}
static int
aac_delete_disk(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_delete_disk_align un;
struct aac_delete_disk *ddisk = &un.d;
struct aac_container *dvp;
int rval = 0;
DBCALLED(softs, 2);
if (ddi_copyin((void *)arg, ddisk, sizeof (*ddisk), mode) != 0)
return (EFAULT);
if (ddisk->container_no >= AAC_MAX_CONTAINERS)
return (EINVAL);
mutex_enter(&softs->io_lock);
dvp = &softs->containers[ddisk->container_no];
/*
* We don't trust the userland to tell us when to delete
* a container, rather we rely on an AIF coming from the
* controller.
*/
if (AAC_DEV_IS_VALID(&dvp->dev)) {
if (dvp->locked)
rval = EBUSY;
}
mutex_exit(&softs->io_lock);
return (rval);
}
/*
* The following function comes from Adaptec to support creation of arrays
* bigger than 2TB.
*/
static int
aac_supported_features(struct aac_softstate *softs, intptr_t arg, int mode)
{
union aac_features_align un;
struct aac_features *f = &un.d;
DBCALLED(softs, 2);
if (ddi_copyin((void *)arg, f, sizeof (*f), mode) != 0)
return (EFAULT);
/*
* When the management driver receives FSACTL_GET_FEATURES ioctl with
* ALL zero in the featuresState, the driver will return the current
* state of all the supported features, the data field will not be
* valid.
* When the management driver receives FSACTL_GET_FEATURES ioctl with
* a specific bit set in the featuresState, the driver will return the
* current state of this specific feature and whatever data that are
* associated with the feature in the data field or perform whatever
* action needed indicates in the data field.
*/
if (f->feat.fValue == 0) {
f->feat.fBits.largeLBA =
(softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
/* TODO: In the future, add other features state here as well */
} else {
if (f->feat.fBits.largeLBA)
f->feat.fBits.largeLBA =
(softs->flags & AAC_FLAGS_LBA_64BIT) ? 1 : 0;
/* TODO: Add other features state and data in the future */
}
if (ddi_copyout(f, (void *)arg, sizeof (*f), mode) != 0)
return (EFAULT);
return (0);
}