static int hba_ioctl_callback()

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;
}