#include "cpqary3.h"
static int32_t cpqary3_ioctl_send_bmiccmd(cpqary3_t *, cpqary3_bmic_pass_t *,
int);
static void cpqary3_ioctl_fil_bmic(CommandList_t *, cpqary3_bmic_pass_t *);
static void cpqary3_ioctl_fil_bmic_sas(CommandList_t *, cpqary3_bmic_pass_t *);
static int32_t cpqary3_ioctl_send_scsicmd(cpqary3_t *, cpqary3_scsi_pass_t *,
int);
static void cpqary3_ioctl_fil_scsi(CommandList_t *, cpqary3_scsi_pass_t *);
cpqary3_driver_info_t gdriver_info = {0};
int32_t
cpqary3_ioctl_driver_info(uintptr_t ioctl_reqp, int mode)
{
cpqary3_ioctl_request_t *request;
request = (cpqary3_ioctl_request_t *)
MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
if (NULL == request)
return (FAILURE);
if (ddi_copyin((void *)ioctl_reqp, (void *)request,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
(void) strcpy(gdriver_info.name, "cpqary3");
gdriver_info.version.minor = CPQARY3_MINOR_REV_NO;
gdriver_info.version.major = CPQARY3_MAJOR_REV_NO;
gdriver_info.version.dd = CPQARY3_REV_MONTH;
gdriver_info.version.mm = CPQARY3_REV_DATE;
gdriver_info.version.yyyy = CPQARY3_REV_YEAR;
gdriver_info.max_num_ctlr = MAX_CTLRS;
if (ddi_copyout((void *)&gdriver_info, (void *)(uintptr_t)request->argp,
sizeof (cpqary3_driver_info_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
if (ddi_copyout((void *)request, (void *)ioctl_reqp,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (SUCCESS);
}
int32_t
cpqary3_ioctl_ctlr_info(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
{
cpqary3_ioctl_request_t *request;
cpqary3_ctlr_info_t *ctlr_info;
request = (cpqary3_ioctl_request_t *)
MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
if (NULL == request)
return (FAILURE);
if (ddi_copyin((void *) ioctl_reqp, (void *)request,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
ctlr_info = (cpqary3_ctlr_info_t *)
MEM_ZALLOC(sizeof (cpqary3_ctlr_info_t));
if (NULL == ctlr_info) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (FAILURE);
}
ctlr_info->subsystem_id = cpqary3p->board_id;
ctlr_info->bus = cpqary3p->bus;
ctlr_info->dev = cpqary3p->dev;
ctlr_info->fun = cpqary3p->fun;
ctlr_info->num_of_tgts = cpqary3p->num_of_targets;
ctlr_info->controller_instance = cpqary3p->instance;
if (ddi_copyout((void *)ctlr_info, (void *)(uintptr_t)request->argp,
sizeof (cpqary3_ctlr_info_t), mode)) {
MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
if (ddi_copyout((void *)request, (void *)ioctl_reqp,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
MEM_SFREE(ctlr_info, sizeof (cpqary3_ctlr_info_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (SUCCESS);
}
int32_t
cpqary3_ioctl_bmic_pass(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
{
cpqary3_ioctl_request_t *request;
cpqary3_bmic_pass_t *bmic_pass;
int32_t retval = SUCCESS;
request = (cpqary3_ioctl_request_t *)
MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
if (NULL == request)
return (FAILURE);
if (ddi_copyin((void *)ioctl_reqp, (void *)request,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
bmic_pass = (cpqary3_bmic_pass_t *)
MEM_ZALLOC(sizeof (cpqary3_bmic_pass_t));
if (NULL == bmic_pass) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (FAILURE);
}
if (ddi_copyin((void *)(uintptr_t)request->argp, (void *)bmic_pass,
sizeof (cpqary3_bmic_pass_t), mode)) {
MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
retval = cpqary3_ioctl_send_bmiccmd(cpqary3p, bmic_pass, mode);
if (ddi_copyout((void *) bmic_pass, (void *)(uintptr_t)request->argp,
sizeof (cpqary3_bmic_pass_t), mode)) {
MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
retval = EFAULT;
}
if (ddi_copyout((void *) request, (void *)ioctl_reqp,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
retval = EFAULT;
}
MEM_SFREE(bmic_pass, sizeof (cpqary3_bmic_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (retval);
}
uint32_t cpqary3_ioctl_wait_ms = 30000;
static int32_t
cpqary3_ioctl_send_bmiccmd(cpqary3_t *cpqary3p,
cpqary3_bmic_pass_t *bmic_pass, int mode)
{
cpqary3_cmdpvt_t *memp = NULL;
CommandList_t *cmdlist = NULL;
int8_t *databuf = NULL;
uint8_t retval = 0;
memp = cpqary3_synccmd_alloc(cpqary3p, bmic_pass->buf_len);
if (memp == NULL)
return (FAILURE);
if (bmic_pass->buf_len > 0) {
databuf = memp->driverdata->sg;
}
cmdlist = memp->cmdlist_memaddr;
if (bmic_pass->io_direction == CPQARY3_SCSI_OUT) {
if (bmic_pass->buf_len > 0) {
if (ddi_copyin((void*)(uintptr_t)(bmic_pass->buf),
(void*)databuf, bmic_pass->buf_len, mode)) {
cpqary3_synccmd_free(cpqary3p, memp);
return (EFAULT);
}
}
}
if (cpqary3p->bddef->bd_flags & SA_BD_SAS) {
cpqary3_ioctl_fil_bmic_sas(cmdlist, bmic_pass);
} else {
cpqary3_ioctl_fil_bmic(cmdlist, bmic_pass);
}
memp->complete = cpqary3_synccmd_complete;
if (cpqary3_synccmd_send(cpqary3p, memp, cpqary3_ioctl_wait_ms,
CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
cpqary3_synccmd_free(cpqary3p, memp);
return (ETIMEDOUT);
}
if (bmic_pass->io_direction == CPQARY3_SCSI_IN) {
if (bmic_pass->buf_len > 0) {
if (ddi_copyout((void *)databuf,
(void *)(uintptr_t)bmic_pass->buf,
bmic_pass->buf_len, mode)) {
retval = EFAULT;
}
}
}
if (cmdlist->Header.Tag.drvinfo_n_err == CPQARY3_SYNCCMD_FAILURE) {
bmic_pass->err_status = 1;
bcopy((caddr_t)memp->errorinfop, &bmic_pass->err_info,
sizeof (ErrorInfo_t));
switch (memp->errorinfop->CommandStatus) {
case CISS_CMD_DATA_OVERRUN :
case CISS_CMD_DATA_UNDERRUN :
case CISS_CMD_SUCCESS :
case CISS_CMD_TARGET_STATUS :
retval = SUCCESS;
break;
default :
retval = EIO;
break;
}
}
cpqary3_synccmd_free(cpqary3p, memp);
return (retval);
}
static void
cpqary3_ioctl_fil_bmic(CommandList_t *cmdlist,
cpqary3_bmic_pass_t *bmic_pass)
{
cmdlist->Header.SGTotal = 1;
cmdlist->Header.SGList = 1;
cmdlist->Request.CDBLen = bmic_pass->cmd_len;
cmdlist->Request.Timeout = bmic_pass->timeout;
cmdlist->Request.Type.Type = CISS_TYPE_CMD;
cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
switch (bmic_pass->io_direction) {
case CPQARY3_SCSI_OUT:
cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
break;
case CPQARY3_SCSI_IN:
cmdlist->Request.Type.Direction = CISS_XFER_READ;
break;
case CPQARY3_NODATA_XFER:
cmdlist->Request.Type.Direction = CISS_XFER_NONE;
break;
default:
cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
break;
}
cmdlist ->Request.CDB[0] =
(bmic_pass->io_direction == CPQARY3_SCSI_IN) ? 0x26: 0x27;
cmdlist ->Request.CDB[1] = bmic_pass->unit_number;
cmdlist->Request.CDB[2] = (bmic_pass->blk_number >> 24) & 0xff;
cmdlist->Request.CDB[3] = (bmic_pass->blk_number >> 16) & 0xff;
cmdlist->Request.CDB[4] = (bmic_pass->blk_number >> 8) & 0xff;
cmdlist->Request.CDB[5] = bmic_pass->blk_number;
cmdlist->Request.CDB[6] = bmic_pass->cmd;
cmdlist->Request.CDB[7] = (bmic_pass->buf_len >> 8) & 0xff;
cmdlist->Request.CDB[8] = bmic_pass->buf_len & 0xff;
cmdlist->Request.CDB[9] = 0x00;
bcopy(&bmic_pass->lun_addr[0], &(cmdlist->Header.LUN),
sizeof (LUNAddr_t));
cmdlist->SG[0].Len = bmic_pass->buf_len;
}
int32_t
cpqary3_ioctl_scsi_pass(uintptr_t ioctl_reqp, cpqary3_t *cpqary3p, int mode)
{
cpqary3_ioctl_request_t *request;
cpqary3_scsi_pass_t *scsi_pass;
int32_t retval = SUCCESS;
request = (cpqary3_ioctl_request_t *)
MEM_ZALLOC(sizeof (cpqary3_ioctl_request_t));
if (NULL == request)
return (FAILURE);
if (ddi_copyin((void *)ioctl_reqp, (void *)request,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
scsi_pass = (cpqary3_scsi_pass_t *)
MEM_ZALLOC(sizeof (cpqary3_scsi_pass_t));
if (NULL == scsi_pass) {
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (FAILURE);
}
if (ddi_copyin((void *)(uintptr_t)request->argp, (void *)scsi_pass,
sizeof (cpqary3_scsi_pass_t), mode)) {
MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (EFAULT);
}
retval = cpqary3_ioctl_send_scsicmd(cpqary3p, scsi_pass, mode);
if (ddi_copyout((void *)scsi_pass, (void *)(uintptr_t)request->argp,
sizeof (cpqary3_scsi_pass_t), mode)) {
MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
retval = EFAULT;
}
if (ddi_copyout((void *)request, (void *)ioctl_reqp,
sizeof (cpqary3_ioctl_request_t), mode)) {
MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
retval = EFAULT;
}
MEM_SFREE(scsi_pass, sizeof (cpqary3_scsi_pass_t));
MEM_SFREE(request, sizeof (cpqary3_ioctl_request_t));
return (retval);
}
static int32_t
cpqary3_ioctl_send_scsicmd(cpqary3_t *cpqary3p,
cpqary3_scsi_pass_t *scsi_pass, int mode)
{
cpqary3_cmdpvt_t *memp = NULL;
CommandList_t *cmdlist = NULL;
int8_t *databuf = NULL;
uint8_t retval = 0;
NoeBuffer *evt;
uint16_t drive = 0;
memp = cpqary3_synccmd_alloc(cpqary3p, scsi_pass->buf_len);
if (memp == NULL)
return (FAILURE);
if (scsi_pass->buf_len > 0) {
databuf = memp->driverdata->sg;
}
cmdlist = memp->cmdlist_memaddr;
if (scsi_pass->io_direction == CPQARY3_SCSI_OUT) {
if (scsi_pass->buf_len > 0) {
if (ddi_copyin((void*)(uintptr_t)(scsi_pass->buf),
(void*)databuf, scsi_pass->buf_len, mode)) {
cpqary3_synccmd_free(cpqary3p, memp);
return (EFAULT);
}
}
}
cpqary3_ioctl_fil_scsi(cmdlist, scsi_pass);
memp->complete = cpqary3_synccmd_complete;
if (cpqary3_synccmd_send(cpqary3p, memp, cpqary3_ioctl_wait_ms,
CPQARY3_SYNCCMD_SEND_WAITSIG) != 0) {
cpqary3_synccmd_free(cpqary3p, memp);
return (ETIMEDOUT);
}
if (cpqary3p->noe_support == 0 &&
cmdlist->Request.CDB[0] == 0x26 &&
cmdlist->Request.CDB[6] == BMIC_NOTIFY_ON_EVENT) {
evt = (NoeBuffer*)MEM2DRVPVT(memp)->sg;
if (evt->event_class_code == CLASS_LOGICAL_DRIVE &&
evt->event_subclass_code == SUB_CLASS_STATUS &&
evt->event_detail_code == DETAIL_CHANGE &&
evt->event_specific_data[3] == 1) {
drive = *(uint16_t *)(&evt->event_specific_data[0]);
drive = ((drive < CTLR_SCSI_ID) ?
drive : drive + CPQARY3_TGT_ALIGNMENT);
if (cpqary3p && cpqary3p->cpqary3_tgtp[drive]) {
cpqary3p->cpqary3_tgtp[drive]->type =
CPQARY3_TARGET_NONE;
}
}
}
if (scsi_pass->io_direction == CPQARY3_SCSI_IN) {
if (scsi_pass->buf_len > 0) {
if (ddi_copyout((void *)databuf,
(void *)(uintptr_t)scsi_pass->buf,
scsi_pass->buf_len, mode)) {
retval = EFAULT;
}
}
}
if (cmdlist->Header.Tag.drvinfo_n_err == CPQARY3_SYNCCMD_FAILURE) {
scsi_pass->err_status = 1;
bcopy((caddr_t)memp->errorinfop, &scsi_pass->err_info,
sizeof (ErrorInfo_t));
switch (memp->errorinfop->CommandStatus) {
case CISS_CMD_DATA_OVERRUN:
case CISS_CMD_DATA_UNDERRUN:
case CISS_CMD_SUCCESS:
case CISS_CMD_TARGET_STATUS:
retval = SUCCESS;
break;
default:
retval = EIO;
break;
}
}
cpqary3_synccmd_free(cpqary3p, memp);
return (retval);
}
static void
cpqary3_ioctl_fil_scsi(CommandList_t *cmdlist,
cpqary3_scsi_pass_t *scsi_pass)
{
cmdlist->Header.SGTotal = 1;
cmdlist->Header.SGList = 1;
cmdlist->Request.CDBLen = scsi_pass->cdb_len;
cmdlist->Request.Timeout = scsi_pass->timeout;
cmdlist->Request.Type.Type = CISS_TYPE_CMD;
cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
switch (scsi_pass->io_direction) {
case CPQARY3_SCSI_OUT:
cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
break;
case CPQARY3_SCSI_IN:
cmdlist->Request.Type.Direction = CISS_XFER_READ;
break;
case CPQARY3_NODATA_XFER:
cmdlist->Request.Type.Direction = CISS_XFER_NONE;
break;
default:
cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
break;
}
bcopy(&scsi_pass->cdb[0], &cmdlist->Request.CDB[0],
scsi_pass->cdb_len);
bcopy(&scsi_pass->lun_addr[0], &(cmdlist->Header.LUN),
sizeof (LUNAddr_t));
cmdlist->SG[0].Len = scsi_pass->buf_len;
}
static void
cpqary3_ioctl_fil_bmic_sas(CommandList_t *cmdlist,
cpqary3_bmic_pass_t *bmic_pass)
{
cmdlist->Header.SGTotal = 1;
cmdlist->Header.SGList = 1;
cmdlist->Request.CDBLen = bmic_pass->cmd_len;
cmdlist->Request.Timeout = bmic_pass->timeout;
cmdlist->Request.Type.Type = CISS_TYPE_CMD;
cmdlist->Request.Type.Attribute = CISS_ATTR_HEADOFQUEUE;
switch (bmic_pass->io_direction) {
case CPQARY3_SCSI_OUT:
cmdlist->Request.Type.Direction = CISS_XFER_WRITE;
break;
case CPQARY3_SCSI_IN:
cmdlist->Request.Type.Direction = CISS_XFER_READ;
break;
case CPQARY3_NODATA_XFER:
cmdlist->Request.Type.Direction = CISS_XFER_NONE;
break;
default:
cmdlist->Request.Type.Direction = CISS_XFER_RSVD;
break;
}
cmdlist->Request.CDB[0] =
(bmic_pass->io_direction == CPQARY3_SCSI_IN) ? 0x26: 0x27;
cmdlist->Request.CDB[1] = bmic_pass->unit_number;
cmdlist->Request.CDB[2] = (bmic_pass->blk_number >> 24) & 0xff;
cmdlist->Request.CDB[3] = (bmic_pass->blk_number >> 16) & 0xff;
cmdlist->Request.CDB[4] = (bmic_pass->blk_number >> 8) & 0xff;
cmdlist->Request.CDB[5] = bmic_pass->blk_number;
cmdlist->Request.CDB[6] = bmic_pass->cmd;
cmdlist->Request.CDB[7] = (bmic_pass->buf_len >> 8) & 0xff;
cmdlist->Request.CDB[8] = bmic_pass->buf_len & 0xff;
cmdlist->Request.CDB[9] = 0x00;
switch (bmic_pass->cmd) {
case HPSAS_ID_PHYSICAL_DRIVE:
case HPSAS_TAPE_INQUIRY:
case HPSAS_SENSE_MP_STAT:
case HPSAS_SET_MP_THRESHOLD:
case HPSAS_MP_PARAM_CONTROL:
case HPSAS_SENSE_DRV_ERR_LOG:
case HPSAS_SET_MP_VALUE:
cmdlist -> Request.CDB[2] = bmic_pass->bmic_index & 0xff;
cmdlist -> Request.CDB[9] = (bmic_pass->bmic_index >>8) & 0xff;
break;
case HPSAS_ID_LOG_DRIVE:
case HPSAS_SENSE_LOG_DRIVE:
case HPSAS_READ:
case HPSAS_WRITE:
case HPSAS_WRITE_THROUGH:
case HPSAS_SENSE_CONFIG:
case HPSAS_SET_CONFIG:
case HPSAS_BYPASS_VOL_STATE:
case HPSAS_CHANGE_CONFIG:
case HPSAS_SENSE_ORIG_CONFIG:
case HPSAS_LABEL_LOG_DRIVE:
cmdlist->Request.CDB[9] = (bmic_pass->unit_number >> 8) & 0xff;
break;
default:
break;
}
bcopy(&bmic_pass->lun_addr[0], &(cmdlist->Header.LUN),
sizeof (LUNAddr_t));
cmdlist->SG[0].Len = bmic_pass->buf_len;
}