]> git.ipfire.org Git - ipfire-2.x.git/blame - src/hwinfo/src/hd/pci.c
Kleiner netter neuer Versuch.
[ipfire-2.x.git] / src / hwinfo / src / hd / pci.c
CommitLineData
a6316ce4
MT
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <errno.h>
6#include <fcntl.h>
7#include <ctype.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10typedef unsigned long kernel_ulong_t;
11#include <linux/types.h>
12#ifdef __UCLIBC__
13#include <linux/pci.h>
14#else
15#include <sys/pci.h>
16#endif
17
18#include "hd.h"
19#include "hd_int.h"
20#include "hddb.h"
21#include "pci.h"
22
23/*
24 * linux/ioport.h
25 */
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
35
36
37/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38 * pci stuff
39 *
40 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
41 */
42
43static struct sysfs_attribute *hd_read_single_sysfs_attribute(char *path, char *name);
44static void get_pci_data(hd_data_t *hd_data);
45static void add_pci_data(hd_data_t *hd_data);
46static void add_driver_info(hd_data_t *hd_data);
47static pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci);
48static unsigned char pci_cfg_byte(pci_t *pci, int fd, unsigned idx);
49static void dump_pci_data(hd_data_t *hd_data);
50
51void hd_scan_sysfs_pci(hd_data_t *hd_data)
52{
53 if(!hd_probe_feature(hd_data, pr_pci)) return;
54
55 hd_data->module = mod_pci;
56
57 /* some clean-up */
58 remove_hd_entries(hd_data);
59 hd_data->pci = NULL;
60
61 PROGRESS(1, 0, "sysfs drivers");
62
63 hd_sysfs_driver_list(hd_data);
64
65 PROGRESS(2, 0, "get sysfs pci data");
66
67 get_pci_data(hd_data);
68 if(hd_data->debug) dump_pci_data(hd_data);
69
70 add_pci_data(hd_data);
71}
72
73
74/*
75 * sysfs_get_device_attr() reads *all* device attributes, then returns the
76 * requested one.
77 *
78 * This leads to problems where some attribute *must not* be read.
79 */
80struct sysfs_attribute *hd_read_single_sysfs_attribute(char *path, char *name)
81{
82 char *attr_path = NULL;
83 struct sysfs_attribute *attr;
84
85 str_printf(&attr_path, 0, "%s/%s", path, name);
86 attr = sysfs_open_attribute(attr_path);
87 free_mem(attr_path);
88
89 sysfs_read_attribute(attr);
90
91 return attr;
92}
93
94
95/*
96 * Get the (raw) PCI data, taken from /sys/bus/pci/.
97 *
98 * Note: non-root users can only read the first 64 bytes (of 256)
99 * of the device headers.
100 */
101void get_pci_data(hd_data_t *hd_data)
102{
103 uint64_t ul0, ul1, ul2;
104 unsigned u, u0, u1, u2, u3;
105 unsigned char nxt;
106 str_list_t *sl;
107 char *s;
108 pci_t *pci;
109 int fd;
110
111 struct sysfs_bus *sf_bus;
112 struct dlist *sf_dev_list;
113 struct sysfs_device *sf_dev;
114 struct sysfs_attribute *attr;
115
116 sf_bus = sysfs_open_bus("pci");
117
118 if(!sf_bus) {
119 ADD2LOG("sysfs: no such bus: pci\n");
120 return;
121 }
122
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) {
125 ADD2LOG(
126 " pci device: name = %s, bus_id = %s, bus = %s\n path = %s\n",
127 sf_dev->name,
128 sf_dev->bus_id,
129 sf_dev->bus,
130 hd_sysfs_id(sf_dev->path)
131 );
132
133 if(sscanf(sf_dev->bus_id, "%x:%x:%x.%x", &u0, &u1, &u2, &u3) != 4) continue;
134
135 pci = add_pci_entry(hd_data, new_mem(sizeof *pci));
136
137 pci->sysfs_id = new_str(sf_dev->path);
138 pci->sysfs_bus_id = new_str(sf_dev->bus_id);
139
140 pci->bus = (u0 << 8) + u1;
141 pci->slot = u2;
142 pci->func = u3;
143
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;
149 }
150 sysfs_close_attribute(attr);
151
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;
155 }
156 sysfs_close_attribute(attr);
157
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;
161 }
162 sysfs_close_attribute(attr);
163
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;
167 }
168 sysfs_close_attribute(attr);
169
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;
173 }
174 sysfs_close_attribute(attr);
175
176 if(hd_attr_uint(attr = hd_read_single_sysfs_attribute(sf_dev->path, "irq"), &ul0, 0)) {
177 ADD2LOG(" irq = %d\n", (unsigned) ul0);
178 pci->irq = ul0;
179 }
180 sysfs_close_attribute(attr);
181
182 sl = hd_attr_list(attr = hd_read_single_sysfs_attribute(sf_dev->path, "resource"));
183 for(u = 0; sl; sl = sl->next, u++) {
184 if(
185 sscanf(sl->str, "0x%"SCNx64" 0x%"SCNx64" 0x%"SCNx64, &ul0, &ul1, &ul2) == 3 &&
186 ul1 &&
187 u < sizeof pci->base_addr / sizeof *pci->base_addr
188 ) {
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;
193 }
194 }
195 sysfs_close_attribute(attr);
196
197 s = NULL;
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);
202
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);
206
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 */
210 }
211
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;
215 }
216
217 if((pci->addr_flags[u] & IORESOURCE_MEM)) {
218 if(!(pci->cmd & PCI_COMMAND_MEMORY)) pci->addr_flags[u] |= IORESOURCE_DISABLED;
219 }
220 }
221
222 /* let's get through the capability list */
223 if(
224 pci->hdr_type == PCI_HEADER_TYPE_NORMAL &&
225 (nxt = pci->data[PCI_CAPABILITY_LIST])
226 ) {
227 /*
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
231 * should suffice).
232 */
233 for(u = 0; u < 16 && nxt && nxt <= 0xfe; u++) {
234 switch(pci_cfg_byte(pci, fd, nxt)) {
235 case PCI_CAP_ID_PM:
236 pci->flags |= (1 << pci_flag_pm);
237 break;
238
239 case PCI_CAP_ID_AGP:
240 pci->flags |= (1 << pci_flag_agp);
241 break;
242 }
243 nxt = pci_cfg_byte(pci, fd, nxt + 1);
244 }
245 }
246 }
247
248 close(fd);
249 }
250 s = free_mem(s);
251
252 pci->rev = pci->data[PCI_REVISION_ID];
253
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;
257 }
258 }
259
260 pci->flags |= (1 << pci_flag_ok);
261 }
262
263 sysfs_close_bus(sf_bus);
264}
265
266
267void add_pci_data(hd_data_t *hd_data)
268{
269 hd_t *hd, *hd2;
270 pci_t *pci, *pnext;
271 hd_res_t *res;
272 unsigned u;
273 char *s, *t;
274
275 PROGRESS(4, 0, "build list");
276
277 for(pci = hd_data->pci; pci; pci = pnext) {
278 pnext = pci->next;
279 hd = add_hd_entry(hd_data, __LINE__, 0);
280
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);
284
285 if(pci->sysfs_bus_id && *pci->sysfs_bus_id) {
286 hd->sysfs_bus_id = new_str(pci->sysfs_bus_id);
287 }
288
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;
295
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;
300 }
301
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);
305 }
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);
309 }
310 hd->revision.id = pci->rev;
311
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;
315 }
316
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);
326 }
327
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);
337 }
338 }
339
340 if(pci->irq) {
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);
346 }
347
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;
352 pci->next = NULL;
353 }
354 hd_data->pci = NULL;
355
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);
359
360 if((t = strrchr(s, '/'))) {
361 *t = 0;
362 if((hd2 = hd_find_sysfs_id(hd_data, s))) {
363 hd->attached_to = hd2->idx;
364 }
365 }
366 free_mem(s);
367 }
368 }
369
370 add_driver_info(hd_data);
371}
372
373
374/*
375 * Add driver info in some special cases.
376 */
377void add_driver_info(hd_data_t *hd_data)
378{
379 hd_t *hd;
380 hd_res_t *res;
381
382 for(hd = hd_data->hd; hd; hd = hd->next) {
383 if(hd->bus.id != bus_pci) continue;
384
385 if(
386 (
387 hd->base_class.id == bc_serial &&
388 hd->sub_class.id == sc_ser_fire
389 ) ||
390 (
391 hd->base_class.id == bc_serial &&
392 hd->sub_class.id == sc_ser_usb
393 )
394 ) {
395 for(res = hd->res; res; res = res->next) {
396 if(res->any.type == res_irq) break;
397 }
398 if(!res) hd->is.notready = 1;
399 continue;
400 }
401 }
402}
403
404
405#if 1
406/*
407 * Store a raw PCI entry; just for convenience.
408 */
409pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci)
410{
411 pci_t **pci = &hd_data->pci;
412
413 while(*pci) pci = &(*pci)->next;
414
415 return *pci = new_pci;
416}
417
418#else
419
420/*
421 * Store a raw PCI entry; just for convenience.
422 *
423 * Reverse order.
424 */
425pci_t *add_pci_entry(hd_data_t *hd_data, pci_t *new_pci)
426{
427 new_pci->next = hd_data->pci;
428
429 return hd_data->pci = new_pci;
430}
431#endif
432
433
434/*
435 * get a byte from pci config space
436 */
437unsigned char pci_cfg_byte(pci_t *pci, int fd, unsigned idx)
438{
439 unsigned char uc;
440
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;
446 pci->data[idx] = uc;
447
448 if(idx >= pci->data_ext_len) pci->data_ext_len = idx + 1;
449
450 return uc;
451}
452/*
453 * Add a dump of all raw PCI data to the global log.
454 */
455void dump_pci_data(hd_data_t *hd_data)
456{
457 pci_t *pci;
458 char *s = NULL;
459 char buf[32];
460 int i, j;
461
462 ADD2LOG("---------- PCI raw data ----------\n");
463
464 for(pci = hd_data->pci; pci; pci = pci->next) {
465
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", "");
470
471 *buf = 0;
472 if(pci->secondary_bus) {
473 sprintf(buf, "->%02x", pci->secondary_bus);
474 }
475
476 ADD2LOG(
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
479 );
480 ADD2LOG(
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
483 );
484
485 s = free_mem(s);
486
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]);
490 }
491 if(pci->rom_base_addr)
492 ADD2LOG(" rom %08"PRIx64"\n", pci->rom_base_addr);
493
494 if(pci->log) ADD2LOG("%s", pci->log);
495
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);
500 ADD2LOG("\n");
501 }
502
503 if(pci->next) ADD2LOG("\n");
504 }
505
506 ADD2LOG("---------- PCI raw data end ----------\n");
507}
508
509
510/*
511 * Parse attribute and return integer value.
512 */
513int hd_attr_uint(struct sysfs_attribute *attr, uint64_t *u, int base)
514{
515 char *s;
516 uint64_t u2;
517 int ok;
518
519 if(!(s = hd_attr_str(attr))) return 0;
520
521 u2 = strtoull(s, &s, base);
522 ok = !*s || isspace(*s) ? 1 : 0;
523
524 if(ok && u) *u = u2;
525
526 return ok;
527}
528
529
530/*
531 * Return attribute as string list.
532 */
533str_list_t *hd_attr_list(struct sysfs_attribute *attr)
534{
535 static str_list_t *sl = NULL;
536
537 free_str_list(sl);
538
539 return sl = hd_split('\n', hd_attr_str(attr));
540}
541
542
543/*
544 * Return attribute as string.
545 */
546char *hd_attr_str(struct sysfs_attribute *attr)
547{
548 return attr ? attr->value : NULL;
549}
550
551
552/*
553 * Remove leading "/sys" from path.
554 */
555char *hd_sysfs_id(char *path)
556{
557 if(!path || !*path) return NULL;
558
559 return strchr(path + 1, '/');
560}
561
562
563/*
564 * Convert '!' to '/'.
565 */
566char *hd_sysfs_name2_dev(char *str)
567{
568 static char *s = NULL;
569
570 if(!str) return NULL;
571
572 free_mem(s);
573 s = str = new_str(str);
574
575 while(*str) {
576 if(*str == '!') *str = '/';
577 str++;
578 }
579
580 return s;
581}
582
583
584/*
585 * Convert '/' to '!'.
586 */
587char *hd_sysfs_dev2_name(char *str)
588{
589 static char *s = NULL;
590
591 if(!str) return NULL;
592
593 free_mem(s);
594 s = str = new_str(str);
595
596 while(*str) {
597 if(*str == '/') *str = '!';
598 str++;
599 }
600
601 return s;
602}
603
604