pciconf.c revision 184140
131921Sbrian/*
231921Sbrian * Copyright 1996 Massachusetts Institute of Technology
331921Sbrian *
431921Sbrian * Permission to use, copy, modify, and distribute this software and
531921Sbrian * its documentation for any purpose and without fee is hereby
631921Sbrian * granted, provided that both the above copyright notice and this
731921Sbrian * permission notice appear in all copies, that both the above
831921Sbrian * copyright notice and this permission notice appear in all
931921Sbrian * supporting documentation, and that the name of M.I.T. not be used
1031921Sbrian * in advertising or publicity pertaining to distribution of the
1131921Sbrian * software without specific, written prior permission.  M.I.T. makes
1231921Sbrian * no representations about the suitability of this software for any
1331921Sbrian * purpose.  It is provided "as is" without express or implied
1431921Sbrian * warranty.
1531921Sbrian *
1631921Sbrian * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
1731921Sbrian * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
1831921Sbrian * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
1931921Sbrian * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2031921Sbrian * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2131921Sbrian * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2231921Sbrian * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2331921Sbrian * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2431921Sbrian * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2531921Sbrian * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2636450Sbrian * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2731061Sbrian * SUCH DAMAGE.
2831061Sbrian */
2931061Sbrian
3031061Sbrian#ifndef lint
3136285Sbrianstatic const char rcsid[] =
3231061Sbrian  "$FreeBSD: head/usr.sbin/pciconf/pciconf.c 184140 2008-10-21 20:57:21Z mav $";
3331061Sbrian#endif /* not lint */
3431061Sbrian
3536285Sbrian#include <sys/types.h>
3631061Sbrian#include <sys/fcntl.h>
3733603Sbrian
3831061Sbrian#include <ctype.h>
3931061Sbrian#include <err.h>
4033603Sbrian#include <stdlib.h>
4133603Sbrian#include <stdio.h>
4233603Sbrian#include <string.h>
4333603Sbrian#include <unistd.h>
4433603Sbrian#include <sys/pciio.h>
4533603Sbrian#include <sys/queue.h>
4631061Sbrian
4731061Sbrian#include <dev/pci/pcireg.h>
4831061Sbrian
4931061Sbrian#include "pathnames.h"
5031061Sbrian#include "pciconf.h"
5131061Sbrian
5231061Sbrianstruct pci_device_info
5331061Sbrian{
5431061Sbrian    TAILQ_ENTRY(pci_device_info)	link;
5531061Sbrian    int					id;
5631061Sbrian    char				*desc;
5731061Sbrian};
5831061Sbrian
5931061Sbrianstruct pci_vendor_info
6031061Sbrian{
6131061Sbrian    TAILQ_ENTRY(pci_vendor_info)	link;
6231343Sbrian    TAILQ_HEAD(,pci_device_info)	devs;
6331061Sbrian    int					id;
6431159Sbrian    char				*desc;
6536285Sbrian};
6636285Sbrian
6731061SbrianTAILQ_HEAD(,pci_vendor_info)	pci_vendors;
6831061Sbrian
6931061Sbrianstatic void list_devs(int verbose, int caps);
7031061Sbrianstatic void list_verbose(struct pci_conf *p);
7131061Sbrianstatic const char *guess_class(struct pci_conf *p);
7231061Sbrianstatic const char *guess_subclass(struct pci_conf *p);
7331061Sbrianstatic int load_vendors(void);
7431061Sbrianstatic void readit(const char *, const char *, int);
7531061Sbrianstatic void writeit(const char *, const char *, const char *, int);
7631061Sbrianstatic void chkattached(const char *, int);
7731343Sbrian
7831061Sbrianstatic int exitstatus = 0;
7931159Sbrian
8036285Sbrianstatic void
8136285Sbrianusage(void)
8231061Sbrian{
8331061Sbrian	fprintf(stderr, "%s\n%s\n%s\n%s\n",
8431061Sbrian		"usage: pciconf -l [-cv]",
8531061Sbrian		"       pciconf -a selector",
8631061Sbrian		"       pciconf -r [-b | -h] selector addr[:addr2]",
8731061Sbrian		"       pciconf -w [-b | -h] selector addr value");
8831061Sbrian	exit (1);
8931061Sbrian}
9031061Sbrian
9131061Sbrianint
9236285Sbrianmain(int argc, char **argv)
9331061Sbrian{
9431061Sbrian	int c;
9531061Sbrian	int listmode, readmode, writemode, attachedmode, caps, verbose;
9631061Sbrian	int byte, isshort;
9731061Sbrian
9831061Sbrian	listmode = readmode = writemode = attachedmode = caps = verbose = byte = isshort = 0;
9931061Sbrian
10031061Sbrian	while ((c = getopt(argc, argv, "abchlrwv")) != -1) {
10131061Sbrian		switch(c) {
10231061Sbrian		case 'a':
10331061Sbrian			attachedmode = 1;
10436285Sbrian			break;
10531061Sbrian
10631061Sbrian		case 'b':
10731061Sbrian			byte = 1;
10831061Sbrian			break;
10931061Sbrian
11031061Sbrian		case 'c':
11131061Sbrian			caps = 1;
11231061Sbrian			break;
11331061Sbrian
11431061Sbrian		case 'h':
11531061Sbrian			isshort = 1;
11636285Sbrian			break;
11731061Sbrian
11831061Sbrian		case 'l':
11931061Sbrian			listmode = 1;
12031061Sbrian			break;
12131061Sbrian
12231061Sbrian		case 'r':
12331061Sbrian			readmode = 1;
12431061Sbrian			break;
12531061Sbrian
12631061Sbrian		case 'w':
12731061Sbrian			writemode = 1;
12836285Sbrian			break;
12931061Sbrian
13031061Sbrian		case 'v':
13131061Sbrian			verbose = 1;
13231061Sbrian			break;
13331061Sbrian
13436285Sbrian		default:
13531061Sbrian			usage();
13631061Sbrian		}
13736285Sbrian	}
13831061Sbrian
13936285Sbrian	if ((listmode && optind != argc)
14031061Sbrian	    || (writemode && optind + 3 != argc)
14136285Sbrian	    || (readmode && optind + 2 != argc)
14236285Sbrian	    || (attachedmode && optind + 1 != argc))
14331061Sbrian		usage();
14436285Sbrian
14531061Sbrian	if (listmode) {
14631061Sbrian		list_devs(verbose, caps);
14731061Sbrian	} else if (attachedmode) {
14831061Sbrian		chkattached(argv[optind],
14932025Sbrian		    byte ? 1 : isshort ? 2 : 4);
15032025Sbrian	} else if (readmode) {
15132025Sbrian		readit(argv[optind], argv[optind + 1],
15232025Sbrian		    byte ? 1 : isshort ? 2 : 4);
15332025Sbrian	} else if (writemode) {
15432025Sbrian		writeit(argv[optind], argv[optind + 1], argv[optind + 2],
15536285Sbrian		    byte ? 1 : isshort ? 2 : 4);
15632025Sbrian	} else {
15732025Sbrian		usage();
15832025Sbrian	}
15932025Sbrian
16032025Sbrian	return exitstatus;
16131343Sbrian}
16231061Sbrian
16331061Sbrianstatic void
16431061Sbrianlist_devs(int verbose, int caps)
16531061Sbrian{
16631343Sbrian	int fd;
16736285Sbrian	struct pci_conf_io pc;
16831061Sbrian	struct pci_conf conf[255], *p;
16931061Sbrian	int none_count = 0;
17031061Sbrian
17131061Sbrian	if (verbose)
17231061Sbrian		load_vendors();
17336450Sbrian
17436450Sbrian	fd = open(_PATH_DEVPCI, caps ? O_RDWR : O_RDONLY, 0);
17536450Sbrian	if (fd < 0)
17636450Sbrian		err(1, "%s", _PATH_DEVPCI);
17736450Sbrian
17836450Sbrian	bzero(&pc, sizeof(struct pci_conf_io));
17936450Sbrian	pc.match_buf_len = sizeof(conf);
18036450Sbrian	pc.matches = conf;
18136450Sbrian
18236450Sbrian	do {
18336450Sbrian		if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
18436450Sbrian			err(1, "ioctl(PCIOCGETCONF)");
18536450Sbrian
18631343Sbrian		/*
18731061Sbrian		 * 255 entries should be more than enough for most people,
18831061Sbrian		 * but if someone has more devices, and then changes things
18931061Sbrian		 * around between ioctls, we'll do the cheezy thing and
19031061Sbrian		 * just bail.  The alternative would be to go back to the
19131343Sbrian		 * beginning of the list, and print things twice, which may
19236285Sbrian		 * not be desireable.
19331061Sbrian		 */
19431061Sbrian		if (pc.status == PCI_GETCONF_LIST_CHANGED) {
19531061Sbrian			warnx("PCI device list changed, please try again");
19633603Sbrian			exitstatus = 1;
19733603Sbrian			close(fd);
19833603Sbrian			return;
19933603Sbrian		} else if (pc.status ==  PCI_GETCONF_ERROR) {
20033603Sbrian			warnx("error returned from PCIOCGETCONF ioctl");
20133603Sbrian			exitstatus = 1;
20236285Sbrian			close(fd);
20333603Sbrian			return;
20436285Sbrian		}
20533603Sbrian		for (p = conf; p < &conf[pc.num_matches]; p++) {
20633603Sbrian			printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x "
20736285Sbrian			    "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
20836285Sbrian			    (p->pd_name && *p->pd_name) ? p->pd_name :
20933603Sbrian			    "none",
21033603Sbrian			    (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
21133603Sbrian			    none_count++, p->pc_sel.pc_domain,
21233603Sbrian			    p->pc_sel.pc_bus, p->pc_sel.pc_dev,
21333603Sbrian			    p->pc_sel.pc_func, (p->pc_class << 16) |
21433603Sbrian			    (p->pc_subclass << 8) | p->pc_progif,
21533603Sbrian			    (p->pc_subdevice << 16) | p->pc_subvendor,
21633603Sbrian			    (p->pc_device << 16) | p->pc_vendor,
21733603Sbrian			    p->pc_revid, p->pc_hdr);
21833603Sbrian			if (verbose)
21933603Sbrian				list_verbose(p);
22033603Sbrian			if (caps)
22133603Sbrian				list_caps(fd, p);
22236285Sbrian		}
22333603Sbrian	} while (pc.status == PCI_GETCONF_MORE_DEVS);
22436285Sbrian
22533603Sbrian	close(fd);
22636285Sbrian}
22733603Sbrian
22833603Sbrianstatic void
22936285Sbrianlist_verbose(struct pci_conf *p)
23036285Sbrian{
23136285Sbrian	struct pci_vendor_info	*vi;
23236285Sbrian	struct pci_device_info	*di;
23336285Sbrian	const char *dp;
23436285Sbrian
23536285Sbrian	TAILQ_FOREACH(vi, &pci_vendors, link) {
23636285Sbrian		if (vi->id == p->pc_vendor) {
23736285Sbrian			printf("    vendor     = '%s'\n", vi->desc);
23836285Sbrian			break;
23936285Sbrian		}
24036285Sbrian	}
24136285Sbrian	if (vi == NULL) {
24236285Sbrian		di = NULL;
24336285Sbrian	} else {
24436285Sbrian		TAILQ_FOREACH(di, &vi->devs, link) {
24536285Sbrian			if (di->id == p->pc_device) {
24636285Sbrian				printf("    device     = '%s'\n", di->desc);
24736285Sbrian				break;
24836285Sbrian			}
24936285Sbrian		}
25036285Sbrian	}
25136285Sbrian	if ((dp = guess_class(p)) != NULL)
25236285Sbrian		printf("    class      = %s\n", dp);
25336285Sbrian	if ((dp = guess_subclass(p)) != NULL)
25436285Sbrian		printf("    subclass   = %s\n", dp);
255}
256
257/*
258 * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
259 */
260static struct
261{
262	int	class;
263	int	subclass;
264	const char *desc;
265} pci_nomatch_tab[] = {
266	{PCIC_OLD,		-1,			"old"},
267	{PCIC_OLD,		PCIS_OLD_NONVGA,	"non-VGA display device"},
268	{PCIC_OLD,		PCIS_OLD_VGA,		"VGA-compatible display device"},
269	{PCIC_STORAGE,		-1,			"mass storage"},
270	{PCIC_STORAGE,		PCIS_STORAGE_SCSI,	"SCSI"},
271	{PCIC_STORAGE,		PCIS_STORAGE_IDE,	"ATA"},
272	{PCIC_STORAGE,		PCIS_STORAGE_FLOPPY,	"floppy disk"},
273	{PCIC_STORAGE,		PCIS_STORAGE_IPI,	"IPI"},
274	{PCIC_STORAGE,		PCIS_STORAGE_RAID,	"RAID"},
275	{PCIC_NETWORK,		-1,			"network"},
276	{PCIC_NETWORK,		PCIS_NETWORK_ETHERNET,	"ethernet"},
277	{PCIC_NETWORK,		PCIS_NETWORK_TOKENRING,	"token ring"},
278	{PCIC_NETWORK,		PCIS_NETWORK_FDDI,	"fddi"},
279	{PCIC_NETWORK,		PCIS_NETWORK_ATM,	"ATM"},
280	{PCIC_NETWORK,		PCIS_NETWORK_ISDN,	"ISDN"},
281	{PCIC_DISPLAY,		-1,			"display"},
282	{PCIC_DISPLAY,		PCIS_DISPLAY_VGA,	"VGA"},
283	{PCIC_DISPLAY,		PCIS_DISPLAY_XGA,	"XGA"},
284	{PCIC_DISPLAY,		PCIS_DISPLAY_3D,	"3D"},
285	{PCIC_MULTIMEDIA,	-1,			"multimedia"},
286	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_VIDEO,	"video"},
287	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_AUDIO,	"audio"},
288	{PCIC_MULTIMEDIA,	PCIS_MULTIMEDIA_TELE,	"telephony"},
289	{PCIC_MEMORY,		-1,			"memory"},
290	{PCIC_MEMORY,		PCIS_MEMORY_RAM,	"RAM"},
291	{PCIC_MEMORY,		PCIS_MEMORY_FLASH,	"flash"},
292	{PCIC_BRIDGE,		-1,			"bridge"},
293	{PCIC_BRIDGE,		PCIS_BRIDGE_HOST,	"HOST-PCI"},
294	{PCIC_BRIDGE,		PCIS_BRIDGE_ISA,	"PCI-ISA"},
295	{PCIC_BRIDGE,		PCIS_BRIDGE_EISA,	"PCI-EISA"},
296	{PCIC_BRIDGE,		PCIS_BRIDGE_MCA,	"PCI-MCA"},
297	{PCIC_BRIDGE,		PCIS_BRIDGE_PCI,	"PCI-PCI"},
298	{PCIC_BRIDGE,		PCIS_BRIDGE_PCMCIA,	"PCI-PCMCIA"},
299	{PCIC_BRIDGE,		PCIS_BRIDGE_NUBUS,	"PCI-NuBus"},
300	{PCIC_BRIDGE,		PCIS_BRIDGE_CARDBUS,	"PCI-CardBus"},
301	{PCIC_BRIDGE,		PCIS_BRIDGE_RACEWAY,	"PCI-RACEway"},
302	{PCIC_SIMPLECOMM,	-1,			"simple comms"},
303	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_UART,	"UART"},	/* could detect 16550 */
304	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_PAR,	"parallel port"},
305	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MULSER,	"multiport serial"},
306	{PCIC_SIMPLECOMM,	PCIS_SIMPLECOMM_MODEM,	"generic modem"},
307	{PCIC_BASEPERIPH,	-1,			"base peripheral"},
308	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PIC,	"interrupt controller"},
309	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_DMA,	"DMA controller"},
310	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_TIMER,	"timer"},
311	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_RTC,	"realtime clock"},
312	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_PCIHOT,	"PCI hot-plug controller"},
313	{PCIC_BASEPERIPH,	PCIS_BASEPERIPH_SDHC,	"SD host controller"},
314	{PCIC_INPUTDEV,		-1,			"input device"},
315	{PCIC_INPUTDEV,		PCIS_INPUTDEV_KEYBOARD,	"keyboard"},
316	{PCIC_INPUTDEV,		PCIS_INPUTDEV_DIGITIZER,"digitizer"},
317	{PCIC_INPUTDEV,		PCIS_INPUTDEV_MOUSE,	"mouse"},
318	{PCIC_INPUTDEV,		PCIS_INPUTDEV_SCANNER,	"scanner"},
319	{PCIC_INPUTDEV,		PCIS_INPUTDEV_GAMEPORT,	"gameport"},
320	{PCIC_DOCKING,		-1,			"docking station"},
321	{PCIC_PROCESSOR,	-1,			"processor"},
322	{PCIC_SERIALBUS,	-1,			"serial bus"},
323	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FW,	"FireWire"},
324	{PCIC_SERIALBUS,	PCIS_SERIALBUS_ACCESS,	"AccessBus"},
325	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SSA,	"SSA"},
326	{PCIC_SERIALBUS,	PCIS_SERIALBUS_USB,	"USB"},
327	{PCIC_SERIALBUS,	PCIS_SERIALBUS_FC,	"Fibre Channel"},
328	{PCIC_SERIALBUS,	PCIS_SERIALBUS_SMBUS,	"SMBus"},
329	{PCIC_WIRELESS,		-1,			"wireless controller"},
330	{PCIC_WIRELESS,		PCIS_WIRELESS_IRDA,	"iRDA"},
331	{PCIC_WIRELESS,		PCIS_WIRELESS_IR,	"IR"},
332	{PCIC_WIRELESS,		PCIS_WIRELESS_RF,	"RF"},
333	{PCIC_INTELLIIO,	-1,			"intelligent I/O controller"},
334	{PCIC_INTELLIIO,	PCIS_INTELLIIO_I2O,	"I2O"},
335	{PCIC_SATCOM,		-1,			"satellite communication"},
336	{PCIC_SATCOM,		PCIS_SATCOM_TV,		"sat TV"},
337	{PCIC_SATCOM,		PCIS_SATCOM_AUDIO,	"sat audio"},
338	{PCIC_SATCOM,		PCIS_SATCOM_VOICE,	"sat voice"},
339	{PCIC_SATCOM,		PCIS_SATCOM_DATA,	"sat data"},
340	{PCIC_CRYPTO,		-1,			"encrypt/decrypt"},
341	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"network/computer crypto"},
342	{PCIC_CRYPTO,		PCIS_CRYPTO_NETCOMP,	"entertainment crypto"},
343	{PCIC_DASP,		-1,			"dasp"},
344	{PCIC_DASP,		PCIS_DASP_DPIO,		"DPIO module"},
345	{0, 0,		NULL}
346};
347
348static const char *
349guess_class(struct pci_conf *p)
350{
351	int	i;
352
353	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
354		if (pci_nomatch_tab[i].class == p->pc_class)
355			return(pci_nomatch_tab[i].desc);
356	}
357	return(NULL);
358}
359
360static const char *
361guess_subclass(struct pci_conf *p)
362{
363	int	i;
364
365	for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
366		if ((pci_nomatch_tab[i].class == p->pc_class) &&
367		    (pci_nomatch_tab[i].subclass == p->pc_subclass))
368			return(pci_nomatch_tab[i].desc);
369	}
370	return(NULL);
371}
372
373static int
374load_vendors(void)
375{
376	const char *dbf;
377	FILE *db;
378	struct pci_vendor_info *cv;
379	struct pci_device_info *cd;
380	char buf[1024], str[1024];
381	char *ch;
382	int id, error;
383
384	/*
385	 * Locate the database and initialise.
386	 */
387	TAILQ_INIT(&pci_vendors);
388	if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
389		dbf = _PATH_PCIVDB;
390	if ((db = fopen(dbf, "r")) == NULL)
391		return(1);
392	cv = NULL;
393	cd = NULL;
394	error = 0;
395
396	/*
397	 * Scan input lines from the database
398	 */
399	for (;;) {
400		if (fgets(buf, sizeof(buf), db) == NULL)
401			break;
402
403		if ((ch = strchr(buf, '#')) != NULL)
404			*ch = '\0';
405		ch = strchr(buf, '\0') - 1;
406		while (ch > buf && isspace(*ch))
407			*ch-- = '\0';
408		if (ch <= buf)
409			continue;
410
411		/* Can't handle subvendor / subdevice entries yet */
412		if (buf[0] == '\t' && buf[1] == '\t')
413			continue;
414
415		/* Check for vendor entry */
416		if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
417			if ((id == 0) || (strlen(str) < 1))
418				continue;
419			if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
420				warn("allocating vendor entry");
421				error = 1;
422				break;
423			}
424			if ((cv->desc = strdup(str)) == NULL) {
425				free(cv);
426				warn("allocating vendor description");
427				error = 1;
428				break;
429			}
430			cv->id = id;
431			TAILQ_INIT(&cv->devs);
432			TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
433			continue;
434		}
435
436		/* Check for device entry */
437		if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
438			if ((id == 0) || (strlen(str) < 1))
439				continue;
440			if (cv == NULL) {
441				warnx("device entry with no vendor!");
442				continue;
443			}
444			if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
445				warn("allocating device entry");
446				error = 1;
447				break;
448			}
449			if ((cd->desc = strdup(str)) == NULL) {
450				free(cd);
451				warn("allocating device description");
452				error = 1;
453				break;
454			}
455			cd->id = id;
456			TAILQ_INSERT_TAIL(&cv->devs, cd, link);
457			continue;
458		}
459
460		/* It's a comment or junk, ignore it */
461	}
462	if (ferror(db))
463		error = 1;
464	fclose(db);
465
466	return(error);
467}
468
469uint32_t
470read_config(int fd, struct pcisel *sel, long reg, int width)
471{
472	struct pci_io pi;
473
474	pi.pi_sel = *sel;
475	pi.pi_reg = reg;
476	pi.pi_width = width;
477
478	if (ioctl(fd, PCIOCREAD, &pi) < 0)
479		err(1, "ioctl(PCIOCREAD)");
480
481	return (pi.pi_data);
482}
483
484static struct pcisel
485getsel(const char *str)
486{
487	char *ep = strchr(str, '@');
488	char *epbase;
489	struct pcisel sel;
490	unsigned long selarr[4];
491	int i;
492
493	if (ep == NULL)
494		ep = (char *)str;
495	else
496		ep++;
497
498	epbase = ep;
499
500	if (strncmp(ep, "pci", 3) == 0) {
501		ep += 3;
502		i = 0;
503		do {
504			selarr[i++] = strtoul(ep, &ep, 10);
505		} while ((*ep == ':' || *ep == '.') && *++ep != '\0' && i < 4);
506
507		if (i > 2)
508			sel.pc_func = selarr[--i];
509		else
510			sel.pc_func = 0;
511		sel.pc_dev = selarr[--i];
512		sel.pc_bus = selarr[--i];
513		if (i > 0)
514			sel.pc_domain = selarr[--i];
515		else
516			sel.pc_domain = 0;
517	}
518	if (*ep != '\x0' || ep == epbase)
519		errx(1, "cannot parse selector %s", str);
520	return sel;
521}
522
523static void
524readone(int fd, struct pcisel *sel, long reg, int width)
525{
526
527	printf("%0*x", width*2, read_config(fd, sel, reg, width));
528}
529
530static void
531readit(const char *name, const char *reg, int width)
532{
533	long rstart;
534	long rend;
535	long r;
536	char *end;
537	int i;
538	int fd;
539	struct pcisel sel;
540
541	fd = open(_PATH_DEVPCI, O_RDWR, 0);
542	if (fd < 0)
543		err(1, "%s", _PATH_DEVPCI);
544
545	rend = rstart = strtol(reg, &end, 0);
546	if (end && *end == ':') {
547		end++;
548		rend = strtol(end, (char **) 0, 0);
549	}
550	sel = getsel(name);
551	for (i = 1, r = rstart; r <= rend; i++, r += width) {
552		readone(fd, &sel, r, width);
553		if (i && !(i % 8))
554			putchar(' ');
555		putchar(i % (16/width) ? ' ' : '\n');
556	}
557	if (i % (16/width) != 1)
558		putchar('\n');
559	close(fd);
560}
561
562static void
563writeit(const char *name, const char *reg, const char *data, int width)
564{
565	int fd;
566	struct pci_io pi;
567
568	pi.pi_sel = getsel(name);
569	pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
570	pi.pi_width = width;
571	pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
572
573	fd = open(_PATH_DEVPCI, O_RDWR, 0);
574	if (fd < 0)
575		err(1, "%s", _PATH_DEVPCI);
576
577	if (ioctl(fd, PCIOCWRITE, &pi) < 0)
578		err(1, "ioctl(PCIOCWRITE)");
579}
580
581static void
582chkattached(const char *name, int width)
583{
584	int fd;
585	struct pci_io pi;
586
587	pi.pi_sel = getsel(name);
588	pi.pi_reg = 0;
589	pi.pi_width = width;
590	pi.pi_data = 0;
591
592	fd = open(_PATH_DEVPCI, O_RDWR, 0);
593	if (fd < 0)
594		err(1, "%s", _PATH_DEVPCI);
595
596	if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
597		err(1, "ioctl(PCIOCATTACHED)");
598
599	exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
600	printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
601}
602