]>
git.ipfire.org Git - people/pmueller/ipfire-2.x.git/blob - src/hwinfo/src/hd/pci.c
10 typedef unsigned long kernel_ulong_t
;
11 #include <linux/types.h>
13 #include <linux/pci.h>
26 #define IORESOURCE_BITS 0x000000ff
27 #define IORESOURCE_IO 0x00000100
28 #define IORESOURCE_MEM 0x00000200
29 #define IORESOURCE_IRQ 0x00000400
30 #define IORESOURCE_DMA 0x00000800
31 #define IORESOURCE_PREFETCH 0x00001000
32 #define IORESOURCE_READONLY 0x00002000
33 #define IORESOURCE_CACHEABLE 0x00004000
34 #define IORESOURCE_DISABLED 0x10000000
37 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
40 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
43 static struct sysfs_attribute
*hd_read_single_sysfs_attribute(char *path
, char *name
);
44 static void get_pci_data(hd_data_t
*hd_data
);
45 static void add_pci_data(hd_data_t
*hd_data
);
46 static void add_driver_info(hd_data_t
*hd_data
);
47 static pci_t
*add_pci_entry(hd_data_t
*hd_data
, pci_t
*new_pci
);
48 static unsigned char pci_cfg_byte(pci_t
*pci
, int fd
, unsigned idx
);
49 static void dump_pci_data(hd_data_t
*hd_data
);
51 void hd_scan_sysfs_pci(hd_data_t
*hd_data
)
53 if(!hd_probe_feature(hd_data
, pr_pci
)) return;
55 hd_data
->module
= mod_pci
;
58 remove_hd_entries(hd_data
);
61 PROGRESS(1, 0, "sysfs drivers");
63 hd_sysfs_driver_list(hd_data
);
65 PROGRESS(2, 0, "get sysfs pci data");
67 get_pci_data(hd_data
);
68 if(hd_data
->debug
) dump_pci_data(hd_data
);
70 add_pci_data(hd_data
);
75 * sysfs_get_device_attr() reads *all* device attributes, then returns the
78 * This leads to problems where some attribute *must not* be read.
80 struct sysfs_attribute
*hd_read_single_sysfs_attribute(char *path
, char *name
)
82 char *attr_path
= NULL
;
83 struct sysfs_attribute
*attr
;
85 str_printf(&attr_path
, 0, "%s/%s", path
, name
);
86 attr
= sysfs_open_attribute(attr_path
);
89 sysfs_read_attribute(attr
);
96 * Get the (raw) PCI data, taken from /sys/bus/pci/.
98 * Note: non-root users can only read the first 64 bytes (of 256)
99 * of the device headers.
101 void get_pci_data(hd_data_t
*hd_data
)
103 uint64_t ul0
, ul1
, ul2
;
104 unsigned u
, u0
, u1
, u2
, u3
;
111 struct sysfs_bus
*sf_bus
;
112 struct dlist
*sf_dev_list
;
113 struct sysfs_device
*sf_dev
;
114 struct sysfs_attribute
*attr
;
116 sf_bus
= sysfs_open_bus("pci");
119 ADD2LOG("sysfs: no such bus: pci\n");
123 sf_dev_list
= sysfs_get_bus_devices(sf_bus
);
124 if(sf_dev_list
) dlist_for_each_data(sf_dev_list
, sf_dev
, struct sysfs_device
) {
126 " pci device: name = %s, bus_id = %s, bus = %s\n path = %s\n",
130 hd_sysfs_id(sf_dev
->path
)
133 if(sscanf(sf_dev
->bus_id
, "%x:%x:%x.%x", &u0
, &u1
, &u2
, &u3
) != 4) continue;
135 pci
= add_pci_entry(hd_data
, new_mem(sizeof *pci
));
137 pci
->sysfs_id
= new_str(sf_dev
->path
);
138 pci
->sysfs_bus_id
= new_str(sf_dev
->bus_id
);
140 pci
->bus
= (u0
<< 8) + u1
;
144 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "class"), &ul0
, 0)) {
145 ADD2LOG(" class = 0x%x\n", (unsigned) ul0
);
146 pci
->prog_if
= ul0
& 0xff;
147 pci
->sub_class
= (ul0
>> 8) & 0xff;
148 pci
->base_class
= (ul0
>> 16) & 0xff;
150 sysfs_close_attribute(attr
);
152 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "vendor"), &ul0
, 0)) {
153 ADD2LOG(" vendor = 0x%x\n", (unsigned) ul0
);
154 pci
->vend
= ul0
& 0xffff;
156 sysfs_close_attribute(attr
);
158 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "device"), &ul0
, 0)) {
159 ADD2LOG(" device = 0x%x\n", (unsigned) ul0
);
160 pci
->dev
= ul0
& 0xffff;
162 sysfs_close_attribute(attr
);
164 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "subsystem_vendor"), &ul0
, 0)) {
165 ADD2LOG(" subvendor = 0x%x\n", (unsigned) ul0
);
166 pci
->sub_vend
= ul0
& 0xffff;
168 sysfs_close_attribute(attr
);
170 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "subsystem_device"), &ul0
, 0)) {
171 ADD2LOG(" subdevice = 0x%x\n", (unsigned) ul0
);
172 pci
->sub_dev
= ul0
& 0xffff;
174 sysfs_close_attribute(attr
);
176 if(hd_attr_uint(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "irq"), &ul0
, 0)) {
177 ADD2LOG(" irq = %d\n", (unsigned) ul0
);
180 sysfs_close_attribute(attr
);
182 sl
= hd_attr_list(attr
= hd_read_single_sysfs_attribute(sf_dev
->path
, "resource"));
183 for(u
= 0; sl
; sl
= sl
->next
, u
++) {
185 sscanf(sl
->str
, "0x%"SCNx64
" 0x%"SCNx64
" 0x%"SCNx64
, &ul0
, &ul1
, &ul2
) == 3 &&
187 u
< sizeof pci
->base_addr
/ sizeof *pci
->base_addr
189 ADD2LOG(" res[%u] = 0x%"PRIx64
" 0x%"PRIx64
" 0x%"PRIx64
"\n", u
, ul0
, ul1
, ul2
);
190 pci
->base_addr
[u
] = ul0
;
191 pci
->base_len
[u
] = ul1
+ 1 - ul0
;
192 pci
->addr_flags
[u
] = ul2
;
195 sysfs_close_attribute(attr
);
198 str_printf(&s
, 0, "%s/config", sf_dev
->path
);
199 if((fd
= open(s
, O_RDONLY
)) != -1) {
200 pci
->data_len
= pci
->data_ext_len
= read(fd
, pci
->data
, 0x40);
201 ADD2LOG(" config[%u]\n", pci
->data_len
);
203 if(pci
->data_len
>= 0x40) {
204 pci
->hdr_type
= pci
->data
[PCI_HEADER_TYPE
] & 0x7f;
205 pci
->cmd
= pci
->data
[PCI_COMMAND
] + (pci
->data
[PCI_COMMAND
+ 1] << 8);
207 if(pci
->hdr_type
== 1 || pci
->hdr_type
== 2) { /* PCI or CB bridge */
208 pci
->secondary_bus
= pci
->data
[PCI_SECONDARY_BUS
];
209 /* PCI_SECONDARY_BUS == PCI_CB_CARD_BUS */
212 for(u
= 0; u
< sizeof pci
->base_addr
/ sizeof *pci
->base_addr
; u
++) {
213 if((pci
->addr_flags
[u
] & IORESOURCE_IO
)) {
214 if(!(pci
->cmd
& PCI_COMMAND_IO
)) pci
->addr_flags
[u
] |= IORESOURCE_DISABLED
;
217 if((pci
->addr_flags
[u
] & IORESOURCE_MEM
)) {
218 if(!(pci
->cmd
& PCI_COMMAND_MEMORY
)) pci
->addr_flags
[u
] |= IORESOURCE_DISABLED
;
222 /* let's get through the capability list */
224 pci
->hdr_type
== PCI_HEADER_TYPE_NORMAL
&&
225 (nxt
= pci
->data
[PCI_CAPABILITY_LIST
])
228 * Cut out after 16 capabilities to avoid infinite recursion due
229 * to (potentially) malformed data. 16 is more or less
230 * arbitrary, though (the capabilities are bits in a byte, so 8 entries
233 for(u
= 0; u
< 16 && nxt
&& nxt
<= 0xfe; u
++) {
234 switch(pci_cfg_byte(pci
, fd
, nxt
)) {
236 pci
->flags
|= (1 << pci_flag_pm
);
240 pci
->flags
|= (1 << pci_flag_agp
);
243 nxt
= pci_cfg_byte(pci
, fd
, nxt
+ 1);
252 pci
->rev
= pci
->data
[PCI_REVISION_ID
];
254 if((pci
->addr_flags
[6] & IORESOURCE_MEM
)) {
255 if(!(pci
->data
[PCI_ROM_ADDRESS
] & PCI_ROM_ADDRESS_ENABLE
)) {
256 pci
->addr_flags
[6] |= IORESOURCE_DISABLED
;
260 pci
->flags
|= (1 << pci_flag_ok
);
263 sysfs_close_bus(sf_bus
);
267 void add_pci_data(hd_data_t
*hd_data
)
275 PROGRESS(4, 0, "build list");
277 for(pci
= hd_data
->pci
; pci
; pci
= pnext
) {
279 hd
= add_hd_entry(hd_data
, __LINE__
, 0);
281 hd
->sysfs_id
= new_str(hd_sysfs_id(pci
->sysfs_id
));
282 s
= hd_sysfs_find_driver(hd_data
, hd
->sysfs_id
, 1);
283 if(s
) add_str_list(&hd
->drivers
, s
);
285 if(pci
->sysfs_bus_id
&& *pci
->sysfs_bus_id
) {
286 hd
->sysfs_bus_id
= new_str(pci
->sysfs_bus_id
);
289 hd
->bus
.id
= bus_pci
;
290 hd
->slot
= pci
->slot
+ (pci
->bus
<< 8);
291 hd
->func
= pci
->func
;
292 hd
->base_class
.id
= pci
->base_class
;
293 hd
->sub_class
.id
= pci
->sub_class
;
294 hd
->prog_if
.id
= pci
->prog_if
;
296 /* fix up old VGA's entries */
297 if(hd
->base_class
.id
== bc_none
&& hd
->sub_class
.id
== 0x01) {
298 hd
->base_class
.id
= bc_display
;
299 hd
->sub_class
.id
= sc_dis_vga
;
302 if(pci
->dev
|| pci
->vend
) {
303 hd
->device
.id
= MAKE_ID(TAG_PCI
, pci
->dev
);
304 hd
->vendor
.id
= MAKE_ID(TAG_PCI
, pci
->vend
);
306 if(pci
->sub_dev
|| pci
->sub_vend
) {
307 hd
->sub_device
.id
= MAKE_ID(TAG_PCI
, pci
->sub_dev
);
308 hd
->sub_vendor
.id
= MAKE_ID(TAG_PCI
, pci
->sub_vend
);
310 hd
->revision
.id
= pci
->rev
;
312 if((u
= device_class(hd_data
, hd
->vendor
.id
, hd
->device
.id
))) {
313 hd
->base_class
.id
= u
>> 8;
314 hd
->sub_class
.id
= u
& 0xff;
317 for(u
= 0; u
< sizeof pci
->base_addr
/ sizeof *pci
->base_addr
; u
++) {
318 if((pci
->addr_flags
[u
] & IORESOURCE_IO
)) {
319 res
= new_mem(sizeof *res
);
320 res
->io
.type
= res_io
;
321 res
->io
.enabled
= pci
->addr_flags
[u
] & IORESOURCE_DISABLED
? 0 : 1;
322 res
->io
.base
= pci
->base_addr
[u
];
323 res
->io
.range
= pci
->base_len
[u
];
324 res
->io
.access
= pci
->addr_flags
[u
] & IORESOURCE_READONLY
? acc_ro
: acc_rw
;
325 add_res_entry(&hd
->res
, res
);
328 if((pci
->addr_flags
[u
] & IORESOURCE_MEM
)) {
329 res
= new_mem(sizeof *res
);
330 res
->mem
.type
= res_mem
;
331 res
->mem
.enabled
= pci
->addr_flags
[u
] & IORESOURCE_DISABLED
? 0 : 1;
332 res
->mem
.base
= pci
->base_addr
[u
];
333 res
->mem
.range
= pci
->base_len
[u
];
334 res
->mem
.access
= pci
->addr_flags
[u
] & IORESOURCE_READONLY
? acc_ro
: acc_rw
;
335 res
->mem
.prefetch
= pci
->addr_flags
[u
] & IORESOURCE_PREFETCH
? flag_yes
: flag_no
;
336 add_res_entry(&hd
->res
, res
);
341 res
= new_mem(sizeof *res
);
342 res
->irq
.type
= res_irq
;
343 res
->irq
.enabled
= 1;
344 res
->irq
.base
= pci
->irq
;
345 add_res_entry(&hd
->res
, res
);
348 hd
->detail
= new_mem(sizeof *hd
->detail
);
349 hd
->detail
->type
= hd_detail_pci
;
350 hd
->detail
->pci
.data
= pci
;
351 if(pci
->flags
& (1 << pci_flag_agp
)) hd
->is
.agp
= 1;
356 for(hd
= hd_data
->hd
; hd
; hd
= hd
->next
) {
357 if(hd
->bus
.id
== bus_pci
&& hd
->sysfs_id
) {
358 s
= new_str(hd
->sysfs_id
);
360 if((t
= strrchr(s
, '/'))) {
362 if((hd2
= hd_find_sysfs_id(hd_data
, s
))) {
363 hd
->attached_to
= hd2
->idx
;
370 add_driver_info(hd_data
);
375 * Add driver info in some special cases.
377 void add_driver_info(hd_data_t
*hd_data
)
382 for(hd
= hd_data
->hd
; hd
; hd
= hd
->next
) {
383 if(hd
->bus
.id
!= bus_pci
) continue;
387 hd
->base_class
.id
== bc_serial
&&
388 hd
->sub_class
.id
== sc_ser_fire
391 hd
->base_class
.id
== bc_serial
&&
392 hd
->sub_class
.id
== sc_ser_usb
395 for(res
= hd
->res
; res
; res
= res
->next
) {
396 if(res
->any
.type
== res_irq
) break;
398 if(!res
) hd
->is
.notready
= 1;
407 * Store a raw PCI entry; just for convenience.
409 pci_t
*add_pci_entry(hd_data_t
*hd_data
, pci_t
*new_pci
)
411 pci_t
**pci
= &hd_data
->pci
;
413 while(*pci
) pci
= &(*pci
)->next
;
415 return *pci
= new_pci
;
421 * Store a raw PCI entry; just for convenience.
425 pci_t
*add_pci_entry(hd_data_t
*hd_data
, pci_t
*new_pci
)
427 new_pci
->next
= hd_data
->pci
;
429 return hd_data
->pci
= new_pci
;
435 * get a byte from pci config space
437 unsigned char pci_cfg_byte(pci_t
*pci
, int fd
, unsigned idx
)
441 if(idx
>= sizeof pci
->data
) return 0;
442 if(idx
< pci
->data_len
) return pci
->data
[idx
];
443 if(idx
< pci
->data_ext_len
&& pci
->data
[idx
]) return pci
->data
[idx
];
444 if(lseek(fd
, idx
, SEEK_SET
) != (off_t
) idx
) return 0;
445 if(read(fd
, &uc
, 1) != 1) return 0;
448 if(idx
>= pci
->data_ext_len
) pci
->data_ext_len
= idx
+ 1;
453 * Add a dump of all raw PCI data to the global log.
455 void dump_pci_data(hd_data_t
*hd_data
)
462 ADD2LOG("---------- PCI raw data ----------\n");
464 for(pci
= hd_data
->pci
; pci
; pci
= pci
->next
) {
466 if(!(pci
->flags
& (1 << pci_flag_ok
))) str_printf(&s
, -1, "oops");
467 if(pci
->flags
& (1 << pci_flag_pm
)) str_printf(&s
, -1, ",pm");
468 if(pci
->flags
& (1 << pci_flag_agp
)) str_printf(&s
, -1, ",agp");
469 if(!s
) str_printf(&s
, 0, "%s", "");
472 if(pci
->secondary_bus
) {
473 sprintf(buf
, "->%02x", pci
->secondary_bus
);
477 "bus %02x%s, slot %02x, func %x, vend:dev:s_vend:s_dev:rev %04x:%04x:%04x:%04x:%02x\n",
478 pci
->bus
, buf
, pci
->slot
, pci
->func
, pci
->vend
, pci
->dev
, pci
->sub_vend
, pci
->sub_dev
, pci
->rev
481 "class %02x, sub_class %02x prog_if %02x, hdr %x, flags <%s>, irq %u\n",
482 pci
->base_class
, pci
->sub_class
, pci
->prog_if
, pci
->hdr_type
, *s
== ',' ? s
+ 1 : s
, pci
->irq
487 for(i
= 0; i
< 6; i
++) {
488 if(pci
->base_addr
[i
] || pci
->base_len
[i
])
489 ADD2LOG(" addr%d %08"PRIx64
", size %08"PRIx64
"\n", i
, pci
->base_addr
[i
], pci
->base_len
[i
]);
491 if(pci
->rom_base_addr
)
492 ADD2LOG(" rom %08"PRIx64
"\n", pci
->rom_base_addr
);
494 if(pci
->log
) ADD2LOG("%s", pci
->log
);
496 for(i
= 0; (unsigned) i
< pci
->data_ext_len
; i
+= 0x10) {
497 ADD2LOG(" %02x: ", i
);
498 j
= pci
->data_ext_len
- i
;
499 hexdump(&hd_data
->log
, 1, j
> 0x10 ? 0x10 : j
, pci
->data
+ i
);
503 if(pci
->next
) ADD2LOG("\n");
506 ADD2LOG("---------- PCI raw data end ----------\n");
511 * Parse attribute and return integer value.
513 int hd_attr_uint(struct sysfs_attribute
*attr
, uint64_t *u
, int base
)
519 if(!(s
= hd_attr_str(attr
))) return 0;
521 u2
= strtoull(s
, &s
, base
);
522 ok
= !*s
|| isspace(*s
) ? 1 : 0;
531 * Return attribute as string list.
533 str_list_t
*hd_attr_list(struct sysfs_attribute
*attr
)
535 static str_list_t
*sl
= NULL
;
539 return sl
= hd_split('\n', hd_attr_str(attr
));
544 * Return attribute as string.
546 char *hd_attr_str(struct sysfs_attribute
*attr
)
548 return attr
? attr
->value
: NULL
;
553 * Remove leading "/sys" from path.
555 char *hd_sysfs_id(char *path
)
557 if(!path
|| !*path
) return NULL
;
559 return strchr(path
+ 1, '/');
564 * Convert '!' to '/'.
566 char *hd_sysfs_name2_dev(char *str
)
568 static char *s
= NULL
;
570 if(!str
) return NULL
;
573 s
= str
= new_str(str
);
576 if(*str
== '!') *str
= '/';
585 * Convert '/' to '!'.
587 char *hd_sysfs_dev2_name(char *str
)
589 static char *s
= NULL
;
591 if(!str
) return NULL
;
594 s
= str
= new_str(str
);
597 if(*str
== '/') *str
= '!';