]>
Commit | Line | Data |
---|---|---|
6d5e39ac MM |
1 | /* |
2 | * The PCI Library -- Capabilities | |
3 | * | |
4 | * Copyright (c) 2008 Martin Mares <mj@ucw.cz> | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <string.h> | |
10 | ||
11 | #include "internal.h" | |
12 | ||
13 | static void | |
14 | pci_add_cap(struct pci_dev *d, unsigned int addr, unsigned int id, unsigned int type) | |
15 | { | |
16 | struct pci_cap *cap = pci_malloc(d->access, sizeof(*cap)); | |
17 | ||
18 | cap->next = d->first_cap; | |
19 | d->first_cap = cap; | |
20 | cap->addr = addr; | |
21 | cap->id = id; | |
22 | cap->type = type; | |
23 | d->access->debug("%04x:%02x:%02x.%d: Found capability %04x of type %d at %04x\n", | |
24 | d->domain, d->bus, d->dev, d->func, id, type, addr); | |
25 | } | |
26 | ||
27 | static void | |
28 | pci_scan_trad_caps(struct pci_dev *d) | |
29 | { | |
30 | word status = pci_read_word(d, PCI_STATUS); | |
31 | byte been_there[256]; | |
32 | int where; | |
33 | ||
34 | if (!(status & PCI_STATUS_CAP_LIST)) | |
35 | return; | |
36 | ||
37 | memset(been_there, 0, 256); | |
38 | where = pci_read_byte(d, PCI_CAPABILITY_LIST) & ~3; | |
39 | while (where) | |
40 | { | |
41 | byte id = pci_read_byte(d, where + PCI_CAP_LIST_ID); | |
42 | byte next = pci_read_byte(d, where + PCI_CAP_LIST_NEXT) & ~3; | |
43 | if (been_there[where]++) | |
44 | break; | |
45 | if (id == 0xff) | |
46 | break; | |
47 | pci_add_cap(d, where, id, PCI_CAP_NORMAL); | |
48 | where = next; | |
49 | } | |
50 | } | |
51 | ||
52 | static void | |
53 | pci_scan_ext_caps(struct pci_dev *d) | |
54 | { | |
55 | byte been_there[0x1000]; | |
56 | int where = 0x100; | |
57 | ||
58 | if (!pci_find_cap(d, PCI_CAP_ID_EXP, PCI_CAP_NORMAL)) | |
59 | return; | |
60 | ||
61 | memset(been_there, 0, 0x1000); | |
62 | do | |
63 | { | |
64 | u32 header; | |
65 | int id; | |
66 | ||
67 | header = pci_read_long(d, where); | |
68 | if (!header || header == 0xffffffff) | |
69 | break; | |
70 | id = header & 0xffff; | |
71 | if (been_there[where]++) | |
72 | break; | |
73 | pci_add_cap(d, where, id, PCI_CAP_EXTENDED); | |
d61c4772 | 74 | where = (header >> 20) & ~3; |
6d5e39ac MM |
75 | } |
76 | while (where); | |
77 | } | |
78 | ||
79 | unsigned int | |
80 | pci_scan_caps(struct pci_dev *d, unsigned int want_fields) | |
81 | { | |
82 | if ((want_fields & PCI_FILL_EXT_CAPS) && !(d->known_fields & PCI_FILL_CAPS)) | |
83 | want_fields |= PCI_FILL_CAPS; | |
84 | ||
85 | if (want_fields & PCI_FILL_CAPS) | |
86 | pci_scan_trad_caps(d); | |
87 | if (want_fields & PCI_FILL_EXT_CAPS) | |
88 | pci_scan_ext_caps(d); | |
89 | return want_fields; | |
90 | } | |
91 | ||
92 | void | |
93 | pci_free_caps(struct pci_dev *d) | |
94 | { | |
95 | struct pci_cap *cap; | |
96 | ||
97 | while (cap = d->first_cap) | |
98 | { | |
adb9abcf MM |
99 | d->first_cap = cap->next; |
100 | pci_mfree(cap); | |
6d5e39ac MM |
101 | } |
102 | } | |
103 | ||
104 | struct pci_cap * | |
105 | pci_find_cap(struct pci_dev *d, unsigned int id, unsigned int type) | |
106 | { | |
107 | struct pci_cap *c; | |
108 | ||
52aecc75 | 109 | pci_fill_info_v33(d, ((type == PCI_CAP_NORMAL) ? PCI_FILL_CAPS : PCI_FILL_EXT_CAPS)); |
6d5e39ac MM |
110 | for (c=d->first_cap; c; c=c->next) |
111 | if (c->type == type && c->id == id) | |
112 | return c; | |
113 | return NULL; | |
114 | } |