1 /*======================================================================
3 A utility for dumping resource information for PnP devices
5 lspnp.c 1.9 2006/07/06 15:27:55 MDT
7 The initial developer of the original code is David A. Hinds
8 <dahinds@users.sourceforge.net>. Portions created by David A. Hinds
9 are Copyright (C) 1999 David A. Hinds. All Rights Reserved.
11 (c) Copyright 2006 Hewlett-Packard Development Company, L.P.
12 Bjorn Helgaas <bjorn.helgaas@hp.com>
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License version 2 as
16 published by the Free Software Foundation.
18 Usage: lspnp [-b] [-v[v]] [device #]
20 ======================================================================*/
30 #include <asm/types.h>
32 #include "pnp_resource.h"
34 static int verbose = 0, boot = 0;
40 { 1, "mass storage device" },
41 { 2, "network interface controller" },
42 { 3, "display controller" },
43 { 4, "multimedia controller" },
44 { 5, "memory controller" },
45 { 6, "bridge controller" },
46 { 7, "communications device" },
47 { 8, "system peripheral" },
48 { 9, "input device" },
49 { 10, "service processor" }
51 #define NBASE (sizeof(base_type)/sizeof(base_type[0]))
62 { 2, 1, "token ring" },
71 { 6, 0, "host processor" },
74 { 6, 3, "MicroChannel" },
79 { 7, 1, "AT parallel port" },
80 { 8, 0, "programmable interrupt controller" },
81 { 8, 1, "DMA controller" },
82 { 8, 2, "system timer" },
83 { 8, 3, "real time clock" },
86 { 8, 6, "power management" },
88 { 8, 8, "operator panel" },
90 { 9, 1, "digitizer" },
93 { 10, 0, "general memory" }
95 #define NSUB (sizeof(sub_type)/sizeof(sub_type[0]))
97 static struct eisa_id {
100 struct eisa_id * next;
103 #define swap16(n) ((((n)&0x00ff)<<8) | (((n)&0xff00)>>8))
105 ((((n)&0xff000000)>>24) | (((n)&0x00ff0000)>>8) | \
106 (((n)&0x0000ff00)<<8) | (((n)&0x000000ff)<<24))
108 #if (__BYTE_ORDER == _BIG_ENDIAN)
109 #define flip16(n) swap16(n)
110 #define flip32(n) swap32(n)
112 #define flip16(n) (n)
113 #define flip32(n) (n)
116 /*====================================================================*/
118 #define HEX(id,a) hex[((id)>>a) & 15]
119 #define CHAR(id,a) (0x40 + (((id)>>a) & 31))
121 static char *eisa_str(__u32 id)
123 const char *hex = "0123456789abcdef";
126 str[0] = CHAR(id, 26);
127 str[1] = CHAR(id, 21);
128 str[2] = CHAR(id,16);
129 str[3] = HEX(id, 12);
137 static void load_ids(void)
142 FILE *f = fopen("/usr/share/misc/pnp.ids", "r");
146 while (fgets(s, sizeof(s), f)) {
147 if ((strlen(s) < 9) ||
148 !(isupper(s[0]) && isupper(s[1]) && isupper(s[2]) &&
149 isxdigit(s[3]) && isxdigit(s[4]) && isxdigit(s[5]) &&
150 isxdigit(s[6]))) continue;
151 id = malloc(sizeof(struct eisa_id));
152 strncpy(id->id, s, 7);
153 for (n = 3; n < 7; n++)
154 id->id[n] = tolower(id->id[n]);
156 s[strlen(s)-1] = '\0';
157 for (t = s+7; isspace(*t); t++) ;
158 id->name = strdup(t);
159 id->next = eisa_id; eisa_id = id;
164 static void dump_flags(int flags)
170 printf(" [no disable]");
172 printf(" [no config]");
178 printf(" [bootable]");
182 printf(" [removable]");
183 if ((flags & 0x0180) == 0x0000)
185 if ((flags & 0x0180) == 0x0080)
186 printf(" [dynamic]");
187 if ((flags & 0x0180) == 0x0180)
188 printf(" [dynamic only]");
192 static void dump_class(int t1, int t2)
195 for (i = 0; i < NBASE; i++)
196 if (t1 == base_type[i].base) break;
197 printf("%s: ", (i < NBASE) ? base_type[i].name : "reserved");
198 for (i = 0; i < NSUB; i++)
199 if ((t1 == sub_type[i].base) && (t2 == sub_type[i].sub))
201 printf("%s", (i < NSUB) ? sub_type[i].name : "other");
208 static void dump_version(union pnp_small_resource *r)
210 printf("\tPnP version %d.%d, vendor version %d.%d\n",
211 r->version.pnp>>4, r->version.pnp & 0x0f,
212 r->version.vendor>>4, r->version.vendor & 0x0f);
215 static void dump_ldid(union pnp_small_resource *r, int sz)
217 printf("\tlogical ID %s", eisa_str(r->ldid.id));
219 if (r->ldid.flag0 & PNP_RES_LDID_BOOT)
225 static void dump_gdid(union pnp_small_resource *r)
228 char *eis = eisa_str(r->gdid.id);
231 for (eid = eisa_id; eid; eid = eid->next)
232 if (strcmp(eis, eid->id) == 0) break;
234 printf(" %s\n", eid->name);
239 static void dump_irq(union pnp_small_resource *r, int sz)
241 int mask = flip16(r->irq.mask);
245 } else if (mask & (mask-1)) {
246 printf("mask 0x%04x", mask);
248 printf("%d", ffs(mask)-1);
252 if (r->irq.info & PNP_RES_IRQ_HIGH_EDGE)
253 printf(" [high edge]");
254 if (r->irq.info & PNP_RES_IRQ_LOW_EDGE)
255 printf(" [low edge]");
256 if (r->irq.info & PNP_RES_IRQ_HIGH_LEVEL)
257 printf(" [high level]");
258 if (r->irq.info & PNP_RES_IRQ_LOW_LEVEL)
259 printf(" [low level]");
261 printf(" [high edge]");
267 static void dump_dma(union pnp_small_resource *r)
269 int mask = r->dma.mask;
273 } else if (mask & (mask-1)) {
274 printf("mask 0x%04x", mask);
276 printf("%d", ffs(mask)-1);
279 switch (r->dma.info & PNP_RES_DMA_WIDTH_MASK) {
280 case PNP_RES_DMA_WIDTH_8:
281 printf(" [8 bit]"); break;
282 case PNP_RES_DMA_WIDTH_8_16:
283 printf(" [8/16 bit]"); break;
284 case PNP_RES_DMA_WIDTH_16:
285 printf(" [16 bit]"); break;
287 if (r->dma.info & PNP_RES_DMA_BUSMASTER)
289 if (r->dma.info & PNP_RES_DMA_COUNT_BYTE)
290 printf(" [count byte]");
291 if (r->dma.info & PNP_RES_DMA_COUNT_WORD)
292 printf(" [count word]");
293 switch (r->dma.info & PNP_RES_DMA_SPEED_MASK) {
294 case PNP_RES_DMA_SPEED_COMPAT: printf(" [compat]"); break;
295 case PNP_RES_DMA_SPEED_TYPEA: printf(" [type A]"); break;
296 case PNP_RES_DMA_SPEED_TYPEB: printf(" [type B]"); break;
297 case PNP_RES_DMA_SPEED_TYPEF: printf(" [type F]"); break;
303 static void dump_dep_start(union pnp_small_resource *r, int sz)
305 printf("\t[start dep fn");
307 printf(": priority: ");
308 switch (r->dep_start.priority) {
309 case PNP_RES_CONFIG_GOOD:
310 printf("good"); break;
311 case PNP_RES_CONFIG_ACCEPTABLE:
312 printf("acceptable"); break;
313 case PNP_RES_CONFIG_SUBOPTIMAL:
314 printf("suboptimal"); break;
316 printf("reserved"); break;
322 static void dump_dep_end(union pnp_small_resource *r)
324 printf("\t[end dep fn]\n");
327 static void dump_io(union pnp_small_resource *r)
329 int min = flip16(r->io.min), max = flip16(r->io.max);
334 printf("0x%04x-0x%04x", min, min+r->io.len-1);
336 printf("base 0x%04x-0x%04x align 0x%02x len 0x%02x",
337 min, max, r->io.align, r->io.len);
339 if (r->io.info & PNP_RES_IO_DECODE_16)
340 printf(" [16-bit decode]");
345 static void dump_io_fixed(union pnp_small_resource *r)
347 int base = flip16(r->io_fixed.base);
349 if (r->io_fixed.len == 0)
350 printf("disabled\n");
352 printf("0x%04x-0x%04x\n", base, base+r->io_fixed.len-1);
359 static void dump_mem_info(__u8 info)
361 switch (info & PNP_RES_MEM_WIDTH_MASK) {
362 case PNP_RES_MEM_WIDTH_8:
363 printf(" [8 bit]"); break;
364 case PNP_RES_MEM_WIDTH_16:
365 printf(" [16 bit]"); break;
366 case PNP_RES_MEM_WIDTH_8_16:
367 printf(" [8/16 bit]"); break;
368 case PNP_RES_MEM_WIDTH_32:
369 printf(" [32 bit]"); break;
371 printf((info & PNP_RES_MEM_WRITEABLE) ? " [r/w]" : " [r/o]");
372 if (info & PNP_RES_MEM_CACHEABLE)
373 printf(" [cacheable]");
374 if (info & PNP_RES_MEM_HIGH_ADDRESS)
376 if (info & PNP_RES_MEM_SHADOWABLE)
378 if (info & PNP_RES_MEM_EXPANSION_ROM)
382 static void dump_ansi(union pnp_large_resource *r, int sz)
384 printf("\tidentifier '%.*s'\n", sz, r->ansi.str);
387 static void dump_mem(union pnp_large_resource *r)
389 int min = flip16(r->mem.min) << 8;
390 int max = flip16(r->mem.max) << 8;
391 int align = flip16(r->mem.align), len = flip16(r->mem.len);
396 printf("0x%06x-0x%06x", min, min+len-1);
398 printf("base 0x%06x-%06x, align 0x%04x, len 0x%06x",
399 min, max, align ? align : 0x10000, len<<8);
401 dump_mem_info(r->mem.info);
405 static void dump_mem32(union pnp_large_resource *r)
407 u_int min = flip32(r->mem32.min), max = flip32(r->mem32.max);
408 u_int align = flip32(r->mem32.align), len = flip32(r->mem32.len);
413 printf("0x%08x-0x%08x", min, min+len-1);
415 printf("\tmem base 0x%08x-0x%08x align 0x%06x len 0x%06x",
416 min, max, align, len);
418 dump_mem_info(r->mem32.info);
422 static void dump_mem32_fixed(union pnp_large_resource *r)
424 u_int base = flip32(r->mem32_fixed.base);
425 u_int len = flip32(r->mem32_fixed.len);
430 printf("0x%08x-0x%08x", base, base+len-1);
432 dump_mem_info(r->mem32_fixed.info);
436 /*====================================================================*/
438 static u_char *dump_chain(u_char *buf, int nr)
440 union pnp_resource *p = (union pnp_resource *)buf;
443 while (((u_char *)p < buf+nr) && (tag != PNP_RES_SMTAG_END)) {
444 if (p->lg.tag & PNP_RES_LARGE_ITEM) {
445 union pnp_large_resource *r = &p->lg.d;
446 tag = p->lg.tag & ~PNP_RES_LARGE_ITEM;
447 sz = flip16(p->lg.sz) + 2;
449 case PNP_RES_LGTAG_MEM:
451 case PNP_RES_LGTAG_ID_ANSI:
452 dump_ansi(r, sz-2); break;
453 case PNP_RES_LGTAG_ID_UNICODE:
454 /* dump_unicode(r); */ break;
455 case PNP_RES_LGTAG_MEM32:
456 dump_mem32(r); break;
457 case PNP_RES_LGTAG_MEM32_FIXED:
458 dump_mem32_fixed(r); break;
461 union pnp_small_resource *r = &p->sm.d;
462 tag = (p->sm.tag >> 3); sz = (p->sm.tag & 7);
464 case PNP_RES_SMTAG_VERSION:
465 dump_version(r); break;
466 case PNP_RES_SMTAG_LDID:
467 dump_ldid(r, sz); break;
468 case PNP_RES_SMTAG_CDID:
470 case PNP_RES_SMTAG_IRQ:
471 dump_irq(r, sz); break;
472 case PNP_RES_SMTAG_DMA:
474 case PNP_RES_SMTAG_DEP_START:
475 dump_dep_start(r, sz); break;
476 case PNP_RES_SMTAG_DEP_END:
477 dump_dep_end(r); break;
478 case PNP_RES_SMTAG_IO:
480 case PNP_RES_SMTAG_IO_FIXED:
481 dump_io_fixed(r); break;
484 p = (union pnp_resource *) ((u_char *)p + sz + 1);
489 static void dump_resources(char *name)
492 u_char buf[4096], *p;
495 sprintf(fn, "/proc/bus/pnp/%s%s", (boot ? "boot/" : ""), name);
496 fd = open(fn, O_RDONLY);
497 nr = read(fd, buf, sizeof(buf));
501 printf(" allocated resources:\n");
502 p = dump_chain(buf, nr);
505 printf(" possible resources:\n");
507 p = dump_chain(p, nr);
509 printf(" compatible devices:\n");
510 p = dump_chain(p, nr);
516 static int match_device(char *name, char *match)
523 /* no filter or exact match */
524 if (!match || !strcmp(name, match))
527 /* let "01" match "xx:01" or "01" */
528 dev = strrchr(name, ':');
533 if (!strcmp(dev, match))
536 /* let "1" match "xx:01" or "01" */
539 if (*dev == '\0') /* saw "00", back up to "0" */
541 if (!strcmp(dev, match))
547 #define SYSFS_PATH "/sys/bus/pnp/devices"
549 static void sysfs_dump_resources(char *name)
556 sprintf(buf, "%s/%s/resources", SYSFS_PATH, name);
557 file = fopen(buf, "r");
561 fgets(buf, sizeof(buf), file);
562 printf(" %s", buf); /* "state =" */
563 while (fgets(buf, sizeof(buf), file)) {
564 if (first && verbose > 1) {
565 printf(" allocated resources:\n");
575 sprintf(buf, "%s/%s/options", SYSFS_PATH, name);
576 file = fopen(buf, "r");
580 while (fgets(buf, sizeof(buf), file)) {
582 printf(" possible resources:\n");
589 sprintf(buf, "%s/%s/id", SYSFS_PATH, name);
590 file = fopen(buf, "r");
594 fgets(buf, sizeof(buf), file); /* skip first one */
595 while (fgets(buf, sizeof(buf), file)) {
597 printf(" compatible devices:\n");
600 nl = strchr(buf, '\n');
603 for (eid = eisa_id; eid; eid = eid->next)
604 if (strcmp(buf, eid->id) == 0) break;
605 printf("\t%s %s\n", buf, eid ? eid->name : "(unknown)");
610 static char *sysfs_get_string(char *name, char *object)
618 sprintf(buf, "%s/%s/%s", SYSFS_PATH, name, object);
619 fd = open(buf, O_RDONLY);
622 len = read(fd, buf, 256);
624 nl = strchr(buf, '\n');
630 static int sysfs_dump_basic(char *match)
632 struct dirent **namelist;
637 n = scandir(SYSFS_PATH, &namelist, 0, alphasort);
641 for (i = 0; i < n; i++) {
642 name = namelist[i]->d_name;
643 if (match_device(name, match)) {
644 eis = sysfs_get_string(name, "id");
645 for (eid = eisa_id; eid; eid = eid->next)
646 if (strcmp(eis, eid->id) == 0) break;
647 printf("%s %s %s\n", name, eis, eid ? eid->name : "(unknown)");
649 sysfs_dump_resources(name);
650 if (!match) printf("\n");
661 static int dump_basic(char *match)
663 int id, t1, t2, t3, flags;
665 char name[4], *eis, buf[64];
668 if (sysfs_dump_basic(match) == 0)
671 f = fopen("/proc/bus/pnp/devices", "r");
673 fprintf(stderr, "lspnp: neither %s nor /proc/bus/pnp is available\n",
677 while (fgets(buf, 63, f) != NULL) {
678 sscanf(buf, "%2s %x %x:%x:%x %x", name, &id, &t1, &t2, &t3, &flags);
679 if (!match_device(name, match))
682 printf("%s %7s ", name, eis);
683 for (eid = eisa_id; eid; eid = eid->next)
684 if (strcmp(eis, eid->id) == 0) break;
686 printf("%s", eid->name);
693 dump_resources(name);
694 if (!match) printf("\n");
701 /*====================================================================*/
703 void usage(char *name)
705 fprintf(stderr, "usage: %s [-b] [-v[v]] [device #]\n", name);
709 int main(int argc, char *argv[])
711 int optch, errflg = 0;
713 while ((optch = getopt(argc, argv, "bv")) != -1) {
727 while (optind < argc) {
728 if (dump_basic(argv[optind]) != 0)
734 return dump_basic(NULL);