2 * Copyright 1996 Massachusetts Institute of Technology
4 * Permission to use, copy, modify, and distribute this software and
5 * its documentation for any purpose and without fee is hereby
6 * granted, provided that both the above copyright notice and this
7 * permission notice appear in all copies, that both the above
8 * copyright notice and this permission notice appear in all
9 * supporting documentation, and that the name of M.I.T. not be used
10 * in advertising or publicity pertaining to distribution of the
11 * software without specific, written prior permission. M.I.T. makes
12 * no representations about the suitability of this software for any
13 * purpose. It is provided "as is" without express or implied
16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * $FreeBSD: src/usr.sbin/pciconf/pciconf.c,v 1.11.2.3 2002/09/17 22:09:15 jdp Exp $
30 * $DragonFly: src/usr.sbin/pciconf/pciconf.c,v 1.6 2005/01/01 22:06:25 cpressey Exp $
33 #include <sys/types.h>
34 #include <sys/fcntl.h>
41 #include <sys/pciio.h>
42 #include <sys/queue.h>
44 #include <bus/pci/pcireg.h>
46 #include "pathnames.h"
48 struct pci_device_info
50 TAILQ_ENTRY(pci_device_info
) link
;
55 struct pci_vendor_info
57 TAILQ_ENTRY(pci_vendor_info
) link
;
58 TAILQ_HEAD(,pci_device_info
) devs
;
63 TAILQ_HEAD(,pci_vendor_info
) pci_vendors
;
65 static void list_devs(int vendors
);
66 static void list_verbose(struct pci_conf
*p
);
67 static const char *guess_class(struct pci_conf
*p
);
68 static const char *guess_subclass(struct pci_conf
*p
);
69 static int load_vendors(void);
70 static void readit(const char *, const char *, int);
71 static void writeit(const char *, const char *, const char *, int);
72 static void chkattached(const char *, int);
74 static int exitstatus
= 0;
79 fprintf(stderr
, "%s\n%s\n%s\n%s\n",
80 "usage: pciconf -l [-v]",
81 " pciconf -a selector",
82 " pciconf -r [-b | -h] selector addr[:addr2]",
83 " pciconf -w [-b | -h] selector addr value");
88 main(int argc
, char **argv
)
91 int listmode
, readmode
, writemode
, attachedmode
, verbose
;
94 listmode
= readmode
= writemode
= attachedmode
= verbose
= byte
= isshort
= 0;
96 while ((c
= getopt(argc
, argv
, "alrwbhv")) != -1) {
131 if ((listmode
&& optind
!= argc
)
132 || (writemode
&& optind
+ 3 != argc
)
133 || (readmode
&& optind
+ 2 != argc
)
134 || (attachedmode
&& optind
+ 1 != argc
))
139 } else if(attachedmode
) {
140 chkattached(argv
[optind
],
141 byte
? 1 : isshort
? 2 : 4);
142 } else if(readmode
) {
143 readit(argv
[optind
], argv
[optind
+ 1],
144 byte
? 1 : isshort
? 2 : 4);
145 } else if(writemode
) {
146 writeit(argv
[optind
], argv
[optind
+ 1], argv
[optind
+ 2],
147 byte
? 1 : isshort
? 2 : 4);
156 list_devs(int verbose
)
159 struct pci_conf_io pc
;
160 struct pci_conf conf
[255], *p
;
166 fd
= open(_PATH_DEVPCI
, O_RDWR
, 0);
168 err(1, "%s", _PATH_DEVPCI
);
170 bzero(&pc
, sizeof(struct pci_conf_io
));
171 pc
.match_buf_len
= sizeof(conf
);
175 if (ioctl(fd
, PCIOCGETCONF
, &pc
) == -1)
176 err(1, "ioctl(PCIOCGETCONF)");
179 * 255 entries should be more than enough for most people,
180 * but if someone has more devices, and then changes things
181 * around between ioctls, we'll do the cheezy thing and
182 * just bail. The alternative would be to go back to the
183 * beginning of the list, and print things twice, which may
186 if (pc
.status
== PCI_GETCONF_LIST_CHANGED
) {
187 warnx("PCI device list changed, please try again");
191 } else if (pc
.status
== PCI_GETCONF_ERROR
) {
192 warnx("error returned from PCIOCGETCONF ioctl");
197 for (p
= conf
; p
< &conf
[pc
.num_matches
]; p
++) {
199 printf("%s%d@pci%d:%d:%d:\tclass=0x%06x card=0x%08x "
200 "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
201 (p
->pd_name
&& *p
->pd_name
) ? p
->pd_name
:
203 (p
->pd_name
&& *p
->pd_name
) ? (int)p
->pd_unit
:
205 p
->pc_sel
.pc_bus
, p
->pc_sel
.pc_dev
,
206 p
->pc_sel
.pc_func
, (p
->pc_class
<< 16) |
207 (p
->pc_subclass
<< 8) | p
->pc_progif
,
208 (p
->pc_subdevice
<< 16) | p
->pc_subvendor
,
209 (p
->pc_device
<< 16) | p
->pc_vendor
,
210 p
->pc_revid
, p
->pc_hdr
);
214 } while (pc
.status
== PCI_GETCONF_MORE_DEVS
);
220 list_verbose(struct pci_conf
*p
)
222 struct pci_vendor_info
*vi
;
223 struct pci_device_info
*di
;
226 TAILQ_FOREACH(vi
, &pci_vendors
, link
) {
227 if (vi
->id
== p
->pc_vendor
) {
228 printf(" vendor = '%s'\n", vi
->desc
);
235 TAILQ_FOREACH(di
, &vi
->devs
, link
) {
236 if (di
->id
== p
->pc_device
) {
237 printf(" device = '%s'\n", di
->desc
);
242 if ((dp
= guess_class(p
)) != NULL
)
243 printf(" class = %s\n", dp
);
244 if ((dp
= guess_subclass(p
)) != NULL
)
245 printf(" subclass = %s\n", dp
);
249 * This is a direct cut-and-paste from the table in sys/bus/pci/pcireg.h.
256 } pci_nomatch_tab
[] = {
257 {PCIC_OLD
, -1, "old"},
258 {PCIC_OLD
, PCIS_OLD_NONVGA
, "non-VGA display device"},
259 {PCIC_OLD
, PCIS_OLD_VGA
, "VGA-compatible display device"},
260 {PCIC_STORAGE
, -1, "mass storage"},
261 {PCIC_STORAGE
, PCIS_STORAGE_SCSI
, "SCSI"},
262 {PCIC_STORAGE
, PCIS_STORAGE_IDE
, "ATA"},
263 {PCIC_STORAGE
, PCIS_STORAGE_FLOPPY
, "floppy disk"},
264 {PCIC_STORAGE
, PCIS_STORAGE_IPI
, "IPI"},
265 {PCIC_STORAGE
, PCIS_STORAGE_RAID
, "RAID"},
266 {PCIC_STORAGE
, PCIS_STORAGE_ATA_ADMA
, "ATA"},
267 {PCIC_STORAGE
, PCIS_STORAGE_SATA
, "SATA"},
268 {PCIC_NETWORK
, -1, "network"},
269 {PCIC_NETWORK
, PCIS_NETWORK_ETHERNET
, "ethernet"},
270 {PCIC_NETWORK
, PCIS_NETWORK_TOKENRING
, "token ring"},
271 {PCIC_NETWORK
, PCIS_NETWORK_FDDI
, "fddi"},
272 {PCIC_NETWORK
, PCIS_NETWORK_ATM
, "ATM"},
273 {PCIC_NETWORK
, PCIS_NETWORK_ISDN
, "ISDN"},
274 {PCIC_DISPLAY
, -1, "display"},
275 {PCIC_DISPLAY
, PCIS_DISPLAY_VGA
, "VGA"},
276 {PCIC_DISPLAY
, PCIS_DISPLAY_XGA
, "XGA"},
277 {PCIC_DISPLAY
, PCIS_DISPLAY_3D
, "3D"},
278 {PCIC_MULTIMEDIA
, -1, "multimedia"},
279 {PCIC_MULTIMEDIA
, PCIS_MULTIMEDIA_VIDEO
, "video"},
280 {PCIC_MULTIMEDIA
, PCIS_MULTIMEDIA_AUDIO
, "audio"},
281 {PCIC_MULTIMEDIA
, PCIS_MULTIMEDIA_TELE
, "telephony"},
282 {PCIC_MEMORY
, -1, "memory"},
283 {PCIC_MEMORY
, PCIS_MEMORY_RAM
, "RAM"},
284 {PCIC_MEMORY
, PCIS_MEMORY_FLASH
, "flash"},
285 {PCIC_BRIDGE
, -1, "bridge"},
286 {PCIC_BRIDGE
, PCIS_BRIDGE_HOST
, "HOST-PCI"},
287 {PCIC_BRIDGE
, PCIS_BRIDGE_ISA
, "PCI-ISA"},
288 {PCIC_BRIDGE
, PCIS_BRIDGE_EISA
, "PCI-EISA"},
289 {PCIC_BRIDGE
, PCIS_BRIDGE_MCA
, "PCI-MCA"},
290 {PCIC_BRIDGE
, PCIS_BRIDGE_PCI
, "PCI-PCI"},
291 {PCIC_BRIDGE
, PCIS_BRIDGE_PCMCIA
, "PCI-PCMCIA"},
292 {PCIC_BRIDGE
, PCIS_BRIDGE_NUBUS
, "PCI-NuBus"},
293 {PCIC_BRIDGE
, PCIS_BRIDGE_CARDBUS
, "PCI-CardBus"},
294 {PCIC_BRIDGE
, PCIS_BRIDGE_INFINIBAND
, "PCI-InfiniBand"},
295 {PCIC_BRIDGE
, PCIS_BRIDGE_OTHER
, "PCI-unknown"},
296 {PCIC_SIMPLECOMM
, -1, "simple comms"},
297 {PCIC_SIMPLECOMM
, PCIS_SIMPLECOMM_UART
, "UART"}, /* could detect 16550 */
298 {PCIC_SIMPLECOMM
, PCIS_SIMPLECOMM_PAR
, "parallel port"},
299 {PCIC_SIMPLECOMM
, PCIS_SIMPLECOMM_MULSER
, "multiport serial"},
300 {PCIC_SIMPLECOMM
, PCIS_SIMPLECOMM_MODEM
, "generic modem"},
301 {PCIC_SIMPLECOMM
, PCIS_SIMPLECOMM_SMART_CARD
,"smartcard"},
302 {PCIC_BASEPERIPH
, -1, "base peripheral"},
303 {PCIC_BASEPERIPH
, PCIS_BASEPERIPH_PIC
, "interrupt controller"},
304 {PCIC_BASEPERIPH
, PCIS_BASEPERIPH_DMA
, "DMA controller"},
305 {PCIC_BASEPERIPH
, PCIS_BASEPERIPH_TIMER
, "timer"},
306 {PCIC_BASEPERIPH
, PCIS_BASEPERIPH_RTC
, "realtime clock"},
307 {PCIC_BASEPERIPH
, PCIS_BASEPERIPH_PCIHOT
, "PCI Hotplug"},
308 {PCIC_INPUTDEV
, -1, "input device"},
309 {PCIC_INPUTDEV
, PCIS_INPUTDEV_KEYBOARD
, "keyboard"},
310 {PCIC_INPUTDEV
, PCIS_INPUTDEV_DIGITIZER
,"digitizer"},
311 {PCIC_INPUTDEV
, PCIS_INPUTDEV_MOUSE
, "mouse"},
312 {PCIC_INPUTDEV
, PCIS_INPUTDEV_SCANNER
, "scanner"},
313 {PCIC_INPUTDEV
, PCIS_INPUTDEV_GAMEPORT
, "gameport"},
314 {PCIC_DOCKING
, -1, "docking station"},
315 {PCIC_PROCESSOR
, -1, "processor"},
316 {PCIC_SERIALBUS
, -1, "serial bus"},
317 {PCIC_SERIALBUS
, PCIS_SERIALBUS_FW
, "FireWire"},
318 {PCIC_SERIALBUS
, PCIS_SERIALBUS_ACCESS
, "AccessBus"},
319 {PCIC_SERIALBUS
, PCIS_SERIALBUS_SSA
, "SSA"},
320 {PCIC_SERIALBUS
, PCIS_SERIALBUS_USB
, "USB"},
321 {PCIC_SERIALBUS
, PCIS_SERIALBUS_FC
, "Fibre Channel"},
322 {PCIC_SERIALBUS
, PCIS_SERIALBUS_SMBUS
, "SMBus"},
323 {PCIC_SERIALBUS
, PCIS_SERIALBUS_INFINIBAND
,"InfiniBand"},
324 {PCIC_WIRELESS
, -1, "wireless"},
325 {PCIC_WIRELESS
, PCIS_WIRELESS_IRDA
, "iRDA"},
326 {PCIC_WIRELESS
, PCIS_WIRELESS_IR
, "consumer IR"},
327 {PCIC_WIRELESS
, PCIS_WIRELESS_RF
, "RF controller"},
328 {PCIC_WIRELESS
, PCIS_WIRELESS_BLUETOOTH
,"Bluetooth"},
329 {PCIC_WIRELESS
, PCIS_WIRELESS_BROADBAND
,"broadband"},
330 {PCIC_WIRELESS
, PCIS_WIRELESS_80211A
, "Ethernet (802.11a)"},
331 {PCIC_WIRELESS
, PCIS_WIRELESS_80211B
, "Ethernet (802.11b)"},
332 {PCIC_INTELLIIO
, -1, "I2O"},
333 {PCIC_INTELLIIO
, PCIS_INTELLIIO_I2O
, "I2O 1.0"},
334 {PCIC_SATCOM
, -1, "satellite"},
335 {PCIC_CRYPTO
, -1, "crypto controller"},
336 {PCIC_DASP
, -1, "signal processing"},
341 guess_class(struct pci_conf
*p
)
345 for (i
= 0; pci_nomatch_tab
[i
].desc
!= NULL
; i
++) {
346 if (pci_nomatch_tab
[i
].class == p
->pc_class
)
347 return(pci_nomatch_tab
[i
].desc
);
353 guess_subclass(struct pci_conf
*p
)
357 for (i
= 0; pci_nomatch_tab
[i
].desc
!= NULL
; i
++) {
358 if ((pci_nomatch_tab
[i
].class == p
->pc_class
) &&
359 (pci_nomatch_tab
[i
].subclass
== p
->pc_subclass
))
360 return(pci_nomatch_tab
[i
].desc
);
370 struct pci_vendor_info
*cv
;
371 struct pci_device_info
*cd
;
372 char buf
[100], str
[100];
376 * Locate the database and initialise.
378 TAILQ_INIT(&pci_vendors
);
379 if ((dbf
= getenv("PCICONF_VENDOR_DATABASE")) == NULL
)
381 if ((db
= fopen(dbf
, "r")) == NULL
)
388 * Scan input lines from the database
391 if (fgets(buf
, sizeof(buf
), db
) == NULL
)
394 /* Check for vendor entry */
395 if ((buf
[0] != '\t') && (sscanf(buf
, "%04x\t%[^\n]", &id
, str
) == 2)) {
396 if ((id
== 0) || (strlen(str
) < 1))
398 if ((cv
= malloc(sizeof(struct pci_vendor_info
))) == NULL
) {
399 warn("allocating vendor entry");
403 if ((cv
->desc
= strdup(str
)) == NULL
) {
405 warn("allocating vendor description");
410 TAILQ_INIT(&cv
->devs
);
411 TAILQ_INSERT_TAIL(&pci_vendors
, cv
, link
);
415 /* Check for device entry */
416 if ((buf
[0] == '\t') && (sscanf(buf
+ 1, "%04x\t%[^\n]", &id
, str
) == 2)) {
417 if ((id
== 0) || (strlen(str
) < 1))
420 warnx("device entry with no vendor!");
423 if ((cd
= malloc(sizeof(struct pci_device_info
))) == NULL
) {
424 warn("allocating device entry");
428 if ((cd
->desc
= strdup(str
)) == NULL
) {
430 warn("allocating device description");
435 TAILQ_INSERT_TAIL(&cv
->devs
, cd
, link
);
439 /* It's a comment or junk, ignore it */
450 getsel(const char *str
)
452 char *parse_buf
, *ep
;
453 struct pcisel sel
= { .pc_func
= 0};
455 parse_buf
= strdup(str
);
458 if (strncmp(ep
, "pci", 3) == 0) {
460 sel
.pc_bus
= strtoul(ep
, &ep
, 0);
461 if (!ep
|| *ep
++ != ':')
462 errx(1, "cannot parse selector %s", str
);
463 sel
.pc_dev
= strtoul(ep
, &ep
, 0);
464 if (!ep
|| *ep
!= ':') {
468 sel
.pc_func
= strtoul(ep
, &ep
, 0);
473 if (*ep
|| ep
== parse_buf
)
474 errx(1, "cannot parse selector %s", str
);
481 readone(int fd
, struct pcisel
*sel
, long reg
, int width
)
489 if (ioctl(fd
, PCIOCREAD
, &pi
) < 0)
490 err(1, "ioctl(PCIOCREAD)");
492 printf("0x%08x", pi
.pi_data
);
496 readit(const char *name
, const char *reg
, int width
)
506 fd
= open(_PATH_DEVPCI
, O_RDWR
, 0);
508 err(1, "%s", _PATH_DEVPCI
);
510 rend
= rstart
= strtol(reg
, &end
, 0);
511 if (end
&& *end
== ':') {
513 rend
= strtol(end
, NULL
, 0);
516 for (i
= 1, r
= rstart
; r
<= rend
; i
++, r
+= width
) {
517 readone(fd
, &sel
, r
, width
);
518 putchar(i
% 4 ? ' ' : '\n');
526 writeit(const char *name
, const char *reg
, const char *data
, int width
)
531 pi
.pi_sel
= getsel(name
);
532 pi
.pi_reg
= strtoul(reg
, NULL
, 0); /* XXX error check */
534 pi
.pi_data
= strtoul(data
, NULL
, 0); /* XXX error check */
536 fd
= open(_PATH_DEVPCI
, O_RDWR
, 0);
538 err(1, "%s", _PATH_DEVPCI
);
540 if (ioctl(fd
, PCIOCWRITE
, &pi
) < 0)
541 err(1, "ioctl(PCIOCWRITE)");
545 chkattached (const char *name
, int width
)
550 pi
.pi_sel
= getsel(name
);
555 fd
= open(_PATH_DEVPCI
, O_RDWR
, 0);
557 err(1, "%s", _PATH_DEVPCI
);
559 if (ioctl(fd
, PCIOCATTACHED
, &pi
) < 0)
560 err(1, "ioctl(PCIOCATTACHED)");
562 exitstatus
= pi
.pi_data
? 0 : 2; /* exit(2), if NOT attached */
563 printf("%s: %s%s\n", name
, pi
.pi_data
== 0 ? "not " : "", "attached");