static int vfio_cap_len()

in pci/vfio_pci_config.c [1266:1339]


static int vfio_cap_len(struct vfio_pci_core_device *vdev, u8 cap, u8 pos)
{
	struct pci_dev *pdev = vdev->pdev;
	u32 dword;
	u16 word;
	u8 byte;
	int ret;

	switch (cap) {
	case PCI_CAP_ID_MSI:
		return vfio_msi_cap_len(vdev, pos);
	case PCI_CAP_ID_PCIX:
		ret = pci_read_config_word(pdev, pos + PCI_X_CMD, &word);
		if (ret)
			return pcibios_err_to_errno(ret);

		if (PCI_X_CMD_VERSION(word)) {
			if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) {
				/* Test for extended capabilities */
				pci_read_config_dword(pdev, PCI_CFG_SPACE_SIZE,
						      &dword);
				vdev->extended_caps = (dword != 0);
			}
			return PCI_CAP_PCIX_SIZEOF_V2;
		} else
			return PCI_CAP_PCIX_SIZEOF_V0;
	case PCI_CAP_ID_VNDR:
		/* length follows next field */
		ret = pci_read_config_byte(pdev, pos + PCI_CAP_FLAGS, &byte);
		if (ret)
			return pcibios_err_to_errno(ret);

		return byte;
	case PCI_CAP_ID_EXP:
		if (pdev->cfg_size > PCI_CFG_SPACE_SIZE) {
			/* Test for extended capabilities */
			pci_read_config_dword(pdev, PCI_CFG_SPACE_SIZE, &dword);
			vdev->extended_caps = (dword != 0);
		}

		/* length based on version and type */
		if ((pcie_caps_reg(pdev) & PCI_EXP_FLAGS_VERS) == 1) {
			if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END)
				return 0xc; /* "All Devices" only, no link */
			return PCI_CAP_EXP_ENDPOINT_SIZEOF_V1;
		} else {
			if (pci_pcie_type(pdev) == PCI_EXP_TYPE_RC_END)
				return 0x2c; /* No link */
			return PCI_CAP_EXP_ENDPOINT_SIZEOF_V2;
		}
	case PCI_CAP_ID_HT:
		ret = pci_read_config_byte(pdev, pos + 3, &byte);
		if (ret)
			return pcibios_err_to_errno(ret);

		return (byte & HT_3BIT_CAP_MASK) ?
			HT_CAP_SIZEOF_SHORT : HT_CAP_SIZEOF_LONG;
	case PCI_CAP_ID_SATA:
		ret = pci_read_config_byte(pdev, pos + PCI_SATA_REGS, &byte);
		if (ret)
			return pcibios_err_to_errno(ret);

		byte &= PCI_SATA_REGS_MASK;
		if (byte == PCI_SATA_REGS_INLINE)
			return PCI_SATA_SIZEOF_LONG;
		else
			return PCI_SATA_SIZEOF_SHORT;
	default:
		pci_warn(pdev, "%s: unknown length for PCI cap %#x@%#x\n",
			 __func__, cap, pos);
	}

	return 0;
}