Revision 6f4cbd39 hw/pci.c
b/hw/pci.c | ||
---|---|---|
164 | 164 |
if (version_id >= 2) |
165 | 165 |
for (i = 0; i < 4; i ++) |
166 | 166 |
s->irq_state[i] = qemu_get_be32(f); |
167 |
|
|
168 | 167 |
return 0; |
169 | 168 |
} |
170 | 169 |
|
... | ... | |
895 | 894 |
|
896 | 895 |
return (PCIDevice *)dev; |
897 | 896 |
} |
897 |
|
|
898 |
static int pci_find_space(PCIDevice *pdev, uint8_t size) |
|
899 |
{ |
|
900 |
int offset = PCI_CONFIG_HEADER_SIZE; |
|
901 |
int i; |
|
902 |
for (i = PCI_CONFIG_HEADER_SIZE; i < PCI_CONFIG_SPACE_SIZE; ++i) |
|
903 |
if (pdev->used[i]) |
|
904 |
offset = i + 1; |
|
905 |
else if (i - offset + 1 == size) |
|
906 |
return offset; |
|
907 |
return 0; |
|
908 |
} |
|
909 |
|
|
910 |
static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id, |
|
911 |
uint8_t *prev_p) |
|
912 |
{ |
|
913 |
uint8_t next, prev; |
|
914 |
|
|
915 |
if (!(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST)) |
|
916 |
return 0; |
|
917 |
|
|
918 |
for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]); |
|
919 |
prev = next + PCI_CAP_LIST_NEXT) |
|
920 |
if (pdev->config[next + PCI_CAP_LIST_ID] == cap_id) |
|
921 |
break; |
|
922 |
|
|
923 |
if (prev_p) |
|
924 |
*prev_p = prev; |
|
925 |
return next; |
|
926 |
} |
|
927 |
|
|
928 |
/* Reserve space and add capability to the linked list in pci config space */ |
|
929 |
int pci_add_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) |
|
930 |
{ |
|
931 |
uint8_t offset = pci_find_space(pdev, size); |
|
932 |
uint8_t *config = pdev->config + offset; |
|
933 |
if (!offset) |
|
934 |
return -ENOSPC; |
|
935 |
config[PCI_CAP_LIST_ID] = cap_id; |
|
936 |
config[PCI_CAP_LIST_NEXT] = pdev->config[PCI_CAPABILITY_LIST]; |
|
937 |
pdev->config[PCI_CAPABILITY_LIST] = offset; |
|
938 |
pdev->config[PCI_STATUS] |= PCI_STATUS_CAP_LIST; |
|
939 |
memset(pdev->used + offset, 0xFF, size); |
|
940 |
/* Make capability read-only by default */ |
|
941 |
memset(pdev->wmask + offset, 0, size); |
|
942 |
return offset; |
|
943 |
} |
|
944 |
|
|
945 |
/* Unlink capability from the pci config space. */ |
|
946 |
void pci_del_capability(PCIDevice *pdev, uint8_t cap_id, uint8_t size) |
|
947 |
{ |
|
948 |
uint8_t prev, offset = pci_find_capability_list(pdev, cap_id, &prev); |
|
949 |
if (!offset) |
|
950 |
return; |
|
951 |
pdev->config[prev] = pdev->config[offset + PCI_CAP_LIST_NEXT]; |
|
952 |
/* Make capability writeable again */ |
|
953 |
memset(pdev->wmask + offset, 0xff, size); |
|
954 |
memset(pdev->used + offset, 0, size); |
|
955 |
|
|
956 |
if (!pdev->config[PCI_CAPABILITY_LIST]) |
|
957 |
pdev->config[PCI_STATUS] &= ~PCI_STATUS_CAP_LIST; |
|
958 |
} |
|
959 |
|
|
960 |
/* Reserve space for capability at a known offset (to call after load). */ |
|
961 |
void pci_reserve_capability(PCIDevice *pdev, uint8_t offset, uint8_t size) |
|
962 |
{ |
|
963 |
memset(pdev->used + offset, 0xff, size); |
|
964 |
} |
|
965 |
|
|
966 |
uint8_t pci_find_capability(PCIDevice *pdev, uint8_t cap_id) |
|
967 |
{ |
|
968 |
return pci_find_capability_list(pdev, cap_id, NULL); |
|
969 |
} |
Also available in: Unified diff