in esas2r/esas2r_ioctl.c [744:1201]
static int hba_ioctl_callback(struct esas2r_adapter *a,
struct esas2r_request *rq,
struct esas2r_sg_context *sgc,
void *context)
{
struct atto_ioctl *hi = (struct atto_ioctl *)esas2r_buffered_ioctl;
hi->status = ATTO_STS_SUCCESS;
switch (hi->function) {
case ATTO_FUNC_GET_ADAP_INFO:
{
u8 *class_code = (u8 *)&a->pcid->class;
struct atto_hba_get_adapter_info *gai =
&hi->data.get_adap_info;
if (hi->flags & HBAF_TUNNEL) {
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
if (hi->version > ATTO_VER_GET_ADAP_INFO0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_GET_ADAP_INFO0;
break;
}
memset(gai, 0, sizeof(*gai));
gai->pci.vendor_id = a->pcid->vendor;
gai->pci.device_id = a->pcid->device;
gai->pci.ss_vendor_id = a->pcid->subsystem_vendor;
gai->pci.ss_device_id = a->pcid->subsystem_device;
gai->pci.class_code[0] = class_code[0];
gai->pci.class_code[1] = class_code[1];
gai->pci.class_code[2] = class_code[2];
gai->pci.rev_id = a->pcid->revision;
gai->pci.bus_num = a->pcid->bus->number;
gai->pci.dev_num = PCI_SLOT(a->pcid->devfn);
gai->pci.func_num = PCI_FUNC(a->pcid->devfn);
if (pci_is_pcie(a->pcid)) {
u16 stat;
u32 caps;
pcie_capability_read_word(a->pcid, PCI_EXP_LNKSTA,
&stat);
pcie_capability_read_dword(a->pcid, PCI_EXP_LNKCAP,
&caps);
gai->pci.link_speed_curr =
(u8)(stat & PCI_EXP_LNKSTA_CLS);
gai->pci.link_speed_max =
(u8)(caps & PCI_EXP_LNKCAP_SLS);
gai->pci.link_width_curr =
(u8)((stat & PCI_EXP_LNKSTA_NLW)
>> PCI_EXP_LNKSTA_NLW_SHIFT);
gai->pci.link_width_max =
(u8)((caps & PCI_EXP_LNKCAP_MLW)
>> 4);
}
gai->pci.msi_vector_cnt = 1;
if (a->pcid->msix_enabled)
gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSIX;
else if (a->pcid->msi_enabled)
gai->pci.interrupt_mode = ATTO_GAI_PCIIM_MSI;
else
gai->pci.interrupt_mode = ATTO_GAI_PCIIM_LEGACY;
gai->adap_type = ATTO_GAI_AT_ESASRAID2;
if (test_bit(AF2_THUNDERLINK, &a->flags2))
gai->adap_type = ATTO_GAI_AT_TLSASHBA;
if (test_bit(AF_DEGRADED_MODE, &a->flags))
gai->adap_flags |= ATTO_GAI_AF_DEGRADED;
gai->adap_flags |= ATTO_GAI_AF_SPT_SUPP |
ATTO_GAI_AF_DEVADDR_SUPP;
if (a->pcid->subsystem_device == ATTO_ESAS_R60F
|| a->pcid->subsystem_device == ATTO_ESAS_R608
|| a->pcid->subsystem_device == ATTO_ESAS_R644
|| a->pcid->subsystem_device == ATTO_TSSC_3808E)
gai->adap_flags |= ATTO_GAI_AF_VIRT_SES;
gai->num_ports = ESAS2R_NUM_PHYS;
gai->num_phys = ESAS2R_NUM_PHYS;
strcpy(gai->firmware_rev, a->fw_rev);
strcpy(gai->flash_rev, a->flash_rev);
strcpy(gai->model_name_short, esas2r_get_model_name_short(a));
strcpy(gai->model_name, esas2r_get_model_name(a));
gai->num_targets = ESAS2R_MAX_TARGETS;
gai->num_busses = 1;
gai->num_targsper_bus = gai->num_targets;
gai->num_lunsper_targ = 256;
if (a->pcid->subsystem_device == ATTO_ESAS_R6F0
|| a->pcid->subsystem_device == ATTO_ESAS_R60F)
gai->num_connectors = 4;
else
gai->num_connectors = 2;
gai->adap_flags2 |= ATTO_GAI_AF2_ADAP_CTRL_SUPP;
gai->num_targets_backend = a->num_targets_backend;
gai->tunnel_flags = a->ioctl_tunnel
& (ATTO_GAI_TF_MEM_RW
| ATTO_GAI_TF_TRACE
| ATTO_GAI_TF_SCSI_PASS_THRU
| ATTO_GAI_TF_GET_DEV_ADDR
| ATTO_GAI_TF_PHY_CTRL
| ATTO_GAI_TF_CONN_CTRL
| ATTO_GAI_TF_GET_DEV_INFO);
break;
}
case ATTO_FUNC_GET_ADAP_ADDR:
{
struct atto_hba_get_adapter_address *gaa =
&hi->data.get_adap_addr;
if (hi->flags & HBAF_TUNNEL) {
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
if (hi->version > ATTO_VER_GET_ADAP_ADDR0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_GET_ADAP_ADDR0;
} else if (gaa->addr_type == ATTO_GAA_AT_PORT
|| gaa->addr_type == ATTO_GAA_AT_NODE) {
if (gaa->addr_type == ATTO_GAA_AT_PORT
&& gaa->port_id >= ESAS2R_NUM_PHYS) {
hi->status = ATTO_STS_NOT_APPL;
} else {
memcpy((u64 *)gaa->address,
&a->nvram->sas_addr[0], sizeof(u64));
gaa->addr_len = sizeof(u64);
}
} else {
hi->status = ATTO_STS_INV_PARAM;
}
break;
}
case ATTO_FUNC_MEM_RW:
{
if (hi->flags & HBAF_TUNNEL) {
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
case ATTO_FUNC_TRACE:
{
struct atto_hba_trace *trc = &hi->data.trace;
if (hi->flags & HBAF_TUNNEL) {
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
if (hi->version > ATTO_VER_TRACE1) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_TRACE1;
break;
}
if (trc->trace_type == ATTO_TRC_TT_FWCOREDUMP
&& hi->version >= ATTO_VER_TRACE1) {
if (trc->trace_func == ATTO_TRC_TF_UPLOAD) {
u32 len = hi->data_length;
u32 offset = trc->current_offset;
u32 total_len = ESAS2R_FWCOREDUMP_SZ;
/* Size is zero if a core dump isn't present */
if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
total_len = 0;
if (len > total_len)
len = total_len;
if (offset >= total_len
|| offset + len > total_len
|| len == 0) {
hi->status = ATTO_STS_INV_PARAM;
break;
}
memcpy(trc + 1,
a->fw_coredump_buff + offset,
len);
hi->data_length = len;
} else if (trc->trace_func == ATTO_TRC_TF_RESET) {
memset(a->fw_coredump_buff, 0,
ESAS2R_FWCOREDUMP_SZ);
clear_bit(AF2_COREDUMP_SAVED, &a->flags2);
} else if (trc->trace_func != ATTO_TRC_TF_GET_INFO) {
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
/* Always return all the info we can. */
trc->trace_mask = 0;
trc->current_offset = 0;
trc->total_length = ESAS2R_FWCOREDUMP_SZ;
/* Return zero length buffer if core dump not present */
if (!test_bit(AF2_COREDUMP_SAVED, &a->flags2))
trc->total_length = 0;
} else {
hi->status = ATTO_STS_UNSUPPORTED;
}
break;
}
case ATTO_FUNC_SCSI_PASS_THRU:
{
struct atto_hba_scsi_pass_thru *spt = &hi->data.scsi_pass_thru;
struct scsi_lun lun;
memcpy(&lun, spt->lun, sizeof(struct scsi_lun));
if (hi->flags & HBAF_TUNNEL) {
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
if (hi->version > ATTO_VER_SCSI_PASS_THRU0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_SCSI_PASS_THRU0;
break;
}
if (spt->target_id >= ESAS2R_MAX_TARGETS || !check_lun(lun)) {
hi->status = ATTO_STS_INV_PARAM;
break;
}
esas2r_sgc_init(sgc, a, rq, NULL);
sgc->length = hi->data_length;
sgc->cur_offset += offsetof(struct atto_ioctl, data.byte)
+ sizeof(struct atto_hba_scsi_pass_thru);
/* Finish request initialization */
rq->target_id = (u16)spt->target_id;
rq->vrq->scsi.flags |= cpu_to_le32(spt->lun[1]);
memcpy(rq->vrq->scsi.cdb, spt->cdb, 16);
rq->vrq->scsi.length = cpu_to_le32(hi->data_length);
rq->sense_len = spt->sense_length;
rq->sense_buf = (u8 *)spt->sense_data;
/* NOTE: we ignore spt->timeout */
/*
* always usurp the completion callback since the interrupt
* callback mechanism may be used.
*/
rq->aux_req_cx = hi;
rq->aux_req_cb = rq->comp_cb;
rq->comp_cb = scsi_passthru_comp_cb;
if (spt->flags & ATTO_SPTF_DATA_IN) {
rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_RDD);
} else if (spt->flags & ATTO_SPTF_DATA_OUT) {
rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_WRD);
} else {
if (sgc->length) {
hi->status = ATTO_STS_INV_PARAM;
break;
}
}
if (spt->flags & ATTO_SPTF_ORDERED_Q)
rq->vrq->scsi.flags |=
cpu_to_le32(FCP_CMND_TA_ORDRD_Q);
else if (spt->flags & ATTO_SPTF_HEAD_OF_Q)
rq->vrq->scsi.flags |= cpu_to_le32(FCP_CMND_TA_HEAD_Q);
if (!esas2r_build_sg_list(a, rq, sgc)) {
hi->status = ATTO_STS_OUT_OF_RSRC;
break;
}
esas2r_start_request(a, rq);
return true;
}
case ATTO_FUNC_GET_DEV_ADDR:
{
struct atto_hba_get_device_address *gda =
&hi->data.get_dev_addr;
struct esas2r_target *t;
if (hi->flags & HBAF_TUNNEL) {
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
if (hi->version > ATTO_VER_GET_DEV_ADDR0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_GET_DEV_ADDR0;
break;
}
if (gda->target_id >= ESAS2R_MAX_TARGETS) {
hi->status = ATTO_STS_INV_PARAM;
break;
}
t = a->targetdb + (u16)gda->target_id;
if (t->target_state != TS_PRESENT) {
hi->status = ATTO_STS_FAILED;
} else if (gda->addr_type == ATTO_GDA_AT_PORT) {
if (t->sas_addr == 0) {
hi->status = ATTO_STS_UNSUPPORTED;
} else {
*(u64 *)gda->address = t->sas_addr;
gda->addr_len = sizeof(u64);
}
} else if (gda->addr_type == ATTO_GDA_AT_NODE) {
hi->status = ATTO_STS_NOT_APPL;
} else {
hi->status = ATTO_STS_INV_PARAM;
}
/* update the target ID to the next one present. */
gda->target_id =
esas2r_targ_db_find_next_present(a,
(u16)gda->target_id);
break;
}
case ATTO_FUNC_PHY_CTRL:
case ATTO_FUNC_CONN_CTRL:
{
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
case ATTO_FUNC_ADAP_CTRL:
{
struct atto_hba_adap_ctrl *ac = &hi->data.adap_ctrl;
if (hi->flags & HBAF_TUNNEL) {
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
if (hi->version > ATTO_VER_ADAP_CTRL0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_ADAP_CTRL0;
break;
}
if (ac->adap_func == ATTO_AC_AF_HARD_RST) {
esas2r_reset_adapter(a);
} else if (ac->adap_func != ATTO_AC_AF_GET_STATE) {
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
if (test_bit(AF_CHPRST_NEEDED, &a->flags))
ac->adap_state = ATTO_AC_AS_RST_SCHED;
else if (test_bit(AF_CHPRST_PENDING, &a->flags))
ac->adap_state = ATTO_AC_AS_RST_IN_PROG;
else if (test_bit(AF_DISC_PENDING, &a->flags))
ac->adap_state = ATTO_AC_AS_RST_DISC;
else if (test_bit(AF_DISABLED, &a->flags))
ac->adap_state = ATTO_AC_AS_DISABLED;
else if (test_bit(AF_DEGRADED_MODE, &a->flags))
ac->adap_state = ATTO_AC_AS_DEGRADED;
else
ac->adap_state = ATTO_AC_AS_OK;
break;
}
case ATTO_FUNC_GET_DEV_INFO:
{
struct atto_hba_get_device_info *gdi = &hi->data.get_dev_info;
struct esas2r_target *t;
if (hi->flags & HBAF_TUNNEL) {
if (hba_ioctl_tunnel(a, hi, rq, sgc))
return true;
break;
}
if (hi->version > ATTO_VER_GET_DEV_INFO0) {
hi->status = ATTO_STS_INV_VERSION;
hi->version = ATTO_VER_GET_DEV_INFO0;
break;
}
if (gdi->target_id >= ESAS2R_MAX_TARGETS) {
hi->status = ATTO_STS_INV_PARAM;
break;
}
t = a->targetdb + (u16)gdi->target_id;
/* update the target ID to the next one present. */
gdi->target_id =
esas2r_targ_db_find_next_present(a,
(u16)gdi->target_id);
if (t->target_state != TS_PRESENT) {
hi->status = ATTO_STS_FAILED;
break;
}
hi->status = ATTO_STS_UNSUPPORTED;
break;
}
default:
hi->status = ATTO_STS_INV_FUNC;
break;
}
return false;
}