xref: /freebsd/sys/dev/pci/pci_user.c (revision 38d8c9940bac138b83c5aa0066f0e2623d3a3512)
18983cfbfSMike Smith /*
28983cfbfSMike Smith  * Copyright (c) 1997, Stefan Esser <se@freebsd.org>
38983cfbfSMike Smith  * All rights reserved.
48983cfbfSMike Smith  *
58983cfbfSMike Smith  * Redistribution and use in source and binary forms, with or without
68983cfbfSMike Smith  * modification, are permitted provided that the following conditions
78983cfbfSMike Smith  * are met:
88983cfbfSMike Smith  * 1. Redistributions of source code must retain the above copyright
98983cfbfSMike Smith  *    notice unmodified, this list of conditions, and the following
108983cfbfSMike Smith  *    disclaimer.
118983cfbfSMike Smith  * 2. Redistributions in binary form must reproduce the above copyright
128983cfbfSMike Smith  *    notice, this list of conditions and the following disclaimer in the
138983cfbfSMike Smith  *    documentation and/or other materials provided with the distribution.
148983cfbfSMike Smith  *
158983cfbfSMike Smith  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
168983cfbfSMike Smith  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
178983cfbfSMike Smith  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
188983cfbfSMike Smith  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
198983cfbfSMike Smith  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
208983cfbfSMike Smith  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
218983cfbfSMike Smith  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
228983cfbfSMike Smith  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
238983cfbfSMike Smith  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
248983cfbfSMike Smith  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
258983cfbfSMike Smith  *
268983cfbfSMike Smith  * $FreeBSD$
278983cfbfSMike Smith  *
288983cfbfSMike Smith  */
298983cfbfSMike Smith 
308983cfbfSMike Smith #include "opt_bus.h"	/* XXX trim includes */
318983cfbfSMike Smith 
328983cfbfSMike Smith #include <sys/param.h>
338983cfbfSMike Smith #include <sys/systm.h>
348983cfbfSMike Smith #include <sys/malloc.h>
358983cfbfSMike Smith #include <sys/module.h>
368983cfbfSMike Smith #include <sys/linker.h>
378983cfbfSMike Smith #include <sys/fcntl.h>
388983cfbfSMike Smith #include <sys/conf.h>
398983cfbfSMike Smith #include <sys/kernel.h>
408002488bSRobert Watson #include <sys/proc.h>
418983cfbfSMike Smith #include <sys/queue.h>
428983cfbfSMike Smith #include <sys/types.h>
438983cfbfSMike Smith 
448983cfbfSMike Smith #include <vm/vm.h>
458983cfbfSMike Smith #include <vm/pmap.h>
468983cfbfSMike Smith #include <vm/vm_extern.h>
478983cfbfSMike Smith 
488983cfbfSMike Smith #include <sys/bus.h>
498983cfbfSMike Smith #include <machine/bus.h>
508983cfbfSMike Smith #include <sys/rman.h>
518983cfbfSMike Smith #include <machine/resource.h>
528983cfbfSMike Smith 
538983cfbfSMike Smith #include <sys/pciio.h>
5438d8c994SWarner Losh #include <dev/pci/pcireg.h>
5538d8c994SWarner Losh #include <dev/pci/pcivar.h>
568983cfbfSMike Smith 
578983cfbfSMike Smith #include "pcib_if.h"
588983cfbfSMike Smith #include "pci_if.h"
598983cfbfSMike Smith 
608983cfbfSMike Smith /*
618983cfbfSMike Smith  * This is the user interface to PCI configuration space.
628983cfbfSMike Smith  */
638983cfbfSMike Smith 
64b40ce416SJulian Elischer static d_open_t 	pci_open;
65b40ce416SJulian Elischer static d_close_t	pci_close;
668983cfbfSMike Smith static int	pci_conf_match(struct pci_match_conf *matches, int num_matches,
678983cfbfSMike Smith 			       struct pci_conf *match_buf);
68b40ce416SJulian Elischer static d_ioctl_t	pci_ioctl;
698983cfbfSMike Smith 
701fa4dd2fSStefan Eßer #if __FreeBSD_version < 500000
718983cfbfSMike Smith #define	PCI_CDEV	78
721fa4dd2fSStefan Eßer #else
731fa4dd2fSStefan Eßer #define	PCI_CDEV	MAJOR_AUTO
741fa4dd2fSStefan Eßer #endif
758983cfbfSMike Smith 
768983cfbfSMike Smith struct cdevsw pcicdev = {
777ac40f5fSPoul-Henning Kamp 	.d_open =	pci_open,
787ac40f5fSPoul-Henning Kamp 	.d_close =	pci_close,
797ac40f5fSPoul-Henning Kamp 	.d_ioctl =	pci_ioctl,
807ac40f5fSPoul-Henning Kamp 	.d_name =	"pci",
817ac40f5fSPoul-Henning Kamp 	.d_maj =	PCI_CDEV,
828983cfbfSMike Smith };
838983cfbfSMike Smith 
848983cfbfSMike Smith static int
85b40ce416SJulian Elischer pci_open(dev_t dev, int oflags, int devtype, struct thread *td)
868983cfbfSMike Smith {
878002488bSRobert Watson 	int error;
888002488bSRobert Watson 
898002488bSRobert Watson 	if (oflags & FWRITE) {
90a854ed98SJohn Baldwin 		error = securelevel_gt(td->td_ucred, 0);
918002488bSRobert Watson 		if (error)
928002488bSRobert Watson 			return (error);
938983cfbfSMike Smith 	}
948002488bSRobert Watson 
958002488bSRobert Watson 	return (0);
968983cfbfSMike Smith }
978983cfbfSMike Smith 
988983cfbfSMike Smith static int
99b40ce416SJulian Elischer pci_close(dev_t dev, int flag, int devtype, struct thread *td)
1008983cfbfSMike Smith {
1018983cfbfSMike Smith 	return 0;
1028983cfbfSMike Smith }
1038983cfbfSMike Smith 
1048983cfbfSMike Smith /*
1058983cfbfSMike Smith  * Match a single pci_conf structure against an array of pci_match_conf
1068983cfbfSMike Smith  * structures.  The first argument, 'matches', is an array of num_matches
1078983cfbfSMike Smith  * pci_match_conf structures.  match_buf is a pointer to the pci_conf
1088983cfbfSMike Smith  * structure that will be compared to every entry in the matches array.
1098983cfbfSMike Smith  * This function returns 1 on failure, 0 on success.
1108983cfbfSMike Smith  */
1118983cfbfSMike Smith static int
1128983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches,
1138983cfbfSMike Smith 	       struct pci_conf *match_buf)
1148983cfbfSMike Smith {
1158983cfbfSMike Smith 	int i;
1168983cfbfSMike Smith 
1178983cfbfSMike Smith 	if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0))
1188983cfbfSMike Smith 		return(1);
1198983cfbfSMike Smith 
1208983cfbfSMike Smith 	for (i = 0; i < num_matches; i++) {
1218983cfbfSMike Smith 		/*
1228983cfbfSMike Smith 		 * I'm not sure why someone would do this...but...
1238983cfbfSMike Smith 		 */
1248983cfbfSMike Smith 		if (matches[i].flags == PCI_GETCONF_NO_MATCH)
1258983cfbfSMike Smith 			continue;
1268983cfbfSMike Smith 
1278983cfbfSMike Smith 		/*
1288983cfbfSMike Smith 		 * Look at each of the match flags.  If it's set, do the
1298983cfbfSMike Smith 		 * comparison.  If the comparison fails, we don't have a
1308983cfbfSMike Smith 		 * match, go on to the next item if there is one.
1318983cfbfSMike Smith 		 */
1328983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0)
1338983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus))
1348983cfbfSMike Smith 			continue;
1358983cfbfSMike Smith 
1368983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0)
1378983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev))
1388983cfbfSMike Smith 			continue;
1398983cfbfSMike Smith 
1408983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0)
1418983cfbfSMike Smith 		 && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func))
1428983cfbfSMike Smith 			continue;
1438983cfbfSMike Smith 
1448983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0)
1458983cfbfSMike Smith 		 && (match_buf->pc_vendor != matches[i].pc_vendor))
1468983cfbfSMike Smith 			continue;
1478983cfbfSMike Smith 
1488983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0)
1498983cfbfSMike Smith 		 && (match_buf->pc_device != matches[i].pc_device))
1508983cfbfSMike Smith 			continue;
1518983cfbfSMike Smith 
1528983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0)
1538983cfbfSMike Smith 		 && (match_buf->pc_class != matches[i].pc_class))
1548983cfbfSMike Smith 			continue;
1558983cfbfSMike Smith 
1568983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0)
1578983cfbfSMike Smith 		 && (match_buf->pd_unit != matches[i].pd_unit))
1588983cfbfSMike Smith 			continue;
1598983cfbfSMike Smith 
1608983cfbfSMike Smith 		if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0)
1618983cfbfSMike Smith 		 && (strncmp(matches[i].pd_name, match_buf->pd_name,
1628983cfbfSMike Smith 			     sizeof(match_buf->pd_name)) != 0))
1638983cfbfSMike Smith 			continue;
1648983cfbfSMike Smith 
1658983cfbfSMike Smith 		return(0);
1668983cfbfSMike Smith 	}
1678983cfbfSMike Smith 
1688983cfbfSMike Smith 	return(1);
1698983cfbfSMike Smith }
1708983cfbfSMike Smith 
1718983cfbfSMike Smith static int
172b40ce416SJulian Elischer pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
1738983cfbfSMike Smith {
1748983cfbfSMike Smith 	device_t pci, pcib;
1758983cfbfSMike Smith 	struct pci_io *io;
1768983cfbfSMike Smith 	const char *name;
1778983cfbfSMike Smith 	int error;
1788983cfbfSMike Smith 
179d08239c1SJohn-Mark Gurney 	if (!(flag & FWRITE) && cmd != PCIOCGETCONF)
1808983cfbfSMike Smith 		return EPERM;
1818983cfbfSMike Smith 
182d08239c1SJohn-Mark Gurney 	/* make sure register is in bounds and aligned */
183d08239c1SJohn-Mark Gurney 	if (cmd == PCIOCREAD || cmd == PCIOCWRITE)
184d08239c1SJohn-Mark Gurney 		if (io->pi_reg < 0 || io->pi_reg + io->pi_width > PCI_REGMAX ||
185d08239c1SJohn-Mark Gurney 		    io->pi_reg & (io->pi_width - 1))
186d08239c1SJohn-Mark Gurney 			error = EINVAL;
1878983cfbfSMike Smith 
1888983cfbfSMike Smith 	switch(cmd) {
1898983cfbfSMike Smith 	case PCIOCGETCONF:
1908983cfbfSMike Smith 		{
1918983cfbfSMike Smith 		struct pci_devinfo *dinfo;
1928983cfbfSMike Smith 		struct pci_conf_io *cio;
1938983cfbfSMike Smith 		struct devlist *devlist_head;
1948983cfbfSMike Smith 		struct pci_match_conf *pattern_buf;
1958983cfbfSMike Smith 		int num_patterns;
1968983cfbfSMike Smith 		size_t iolen;
1978983cfbfSMike Smith 		int ionum, i;
1988983cfbfSMike Smith 
1998983cfbfSMike Smith 		cio = (struct pci_conf_io *)data;
2008983cfbfSMike Smith 
2018983cfbfSMike Smith 		num_patterns = 0;
2028983cfbfSMike Smith 		dinfo = NULL;
2038983cfbfSMike Smith 
2048983cfbfSMike Smith 		/*
2058983cfbfSMike Smith 		 * If the user specified an offset into the device list,
2068983cfbfSMike Smith 		 * but the list has changed since they last called this
2078983cfbfSMike Smith 		 * ioctl, tell them that the list has changed.  They will
2088983cfbfSMike Smith 		 * have to get the list from the beginning.
2098983cfbfSMike Smith 		 */
2108983cfbfSMike Smith 		if ((cio->offset != 0)
2118983cfbfSMike Smith 		 && (cio->generation != pci_generation)){
2128983cfbfSMike Smith 			cio->num_matches = 0;
2138983cfbfSMike Smith 			cio->status = PCI_GETCONF_LIST_CHANGED;
2148983cfbfSMike Smith 			error = 0;
2158983cfbfSMike Smith 			break;
2168983cfbfSMike Smith 		}
2178983cfbfSMike Smith 
2188983cfbfSMike Smith 		/*
2198983cfbfSMike Smith 		 * Check to see whether the user has asked for an offset
2208983cfbfSMike Smith 		 * past the end of our list.
2218983cfbfSMike Smith 		 */
2228983cfbfSMike Smith 		if (cio->offset >= pci_numdevs) {
2238983cfbfSMike Smith 			cio->num_matches = 0;
2248983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
2258983cfbfSMike Smith 			error = 0;
2268983cfbfSMike Smith 			break;
2278983cfbfSMike Smith 		}
2288983cfbfSMike Smith 
2298983cfbfSMike Smith 		/* get the head of the device queue */
2308983cfbfSMike Smith 		devlist_head = &pci_devq;
2318983cfbfSMike Smith 
2328983cfbfSMike Smith 		/*
2338983cfbfSMike Smith 		 * Determine how much room we have for pci_conf structures.
2348983cfbfSMike Smith 		 * Round the user's buffer size down to the nearest
2358983cfbfSMike Smith 		 * multiple of sizeof(struct pci_conf) in case the user
2368983cfbfSMike Smith 		 * didn't specify a multiple of that size.
2378983cfbfSMike Smith 		 */
2388983cfbfSMike Smith 		iolen = min(cio->match_buf_len -
2398983cfbfSMike Smith 			    (cio->match_buf_len % sizeof(struct pci_conf)),
2408983cfbfSMike Smith 			    pci_numdevs * sizeof(struct pci_conf));
2418983cfbfSMike Smith 
2428983cfbfSMike Smith 		/*
2438983cfbfSMike Smith 		 * Since we know that iolen is a multiple of the size of
2448983cfbfSMike Smith 		 * the pciconf union, it's okay to do this.
2458983cfbfSMike Smith 		 */
2468983cfbfSMike Smith 		ionum = iolen / sizeof(struct pci_conf);
2478983cfbfSMike Smith 
2488983cfbfSMike Smith 		/*
2498983cfbfSMike Smith 		 * If this test is true, the user wants the pci_conf
2508983cfbfSMike Smith 		 * structures returned to match the supplied entries.
2518983cfbfSMike Smith 		 */
252e3f932deSJohn-Mark Gurney 		if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs)
2538983cfbfSMike Smith 		 && (cio->pat_buf_len > 0)) {
2548983cfbfSMike Smith 			/*
2558983cfbfSMike Smith 			 * pat_buf_len needs to be:
2568983cfbfSMike Smith 			 * num_patterns * sizeof(struct pci_match_conf)
2578983cfbfSMike Smith 			 * While it is certainly possible the user just
2588983cfbfSMike Smith 			 * allocated a large buffer, but set the number of
2598983cfbfSMike Smith 			 * matches correctly, it is far more likely that
2608983cfbfSMike Smith 			 * their kernel doesn't match the userland utility
2618983cfbfSMike Smith 			 * they're using.  It's also possible that the user
2628983cfbfSMike Smith 			 * forgot to initialize some variables.  Yes, this
2638983cfbfSMike Smith 			 * may be overly picky, but I hazard to guess that
2648983cfbfSMike Smith 			 * it's far more likely to just catch folks that
2658983cfbfSMike Smith 			 * updated their kernel but not their userland.
2668983cfbfSMike Smith 			 */
2678983cfbfSMike Smith 			if ((cio->num_patterns *
2688983cfbfSMike Smith 			    sizeof(struct pci_match_conf)) != cio->pat_buf_len){
2698983cfbfSMike Smith 				/* The user made a mistake, return an error*/
2708983cfbfSMike Smith 				cio->status = PCI_GETCONF_ERROR;
2718983cfbfSMike Smith 				cio->num_matches = 0;
2728983cfbfSMike Smith 				error = EINVAL;
2738983cfbfSMike Smith 				break;
2748983cfbfSMike Smith 			}
2758983cfbfSMike Smith 
2768983cfbfSMike Smith 			/*
2778983cfbfSMike Smith 			 * Allocate a buffer to hold the patterns.
2788983cfbfSMike Smith 			 */
2798983cfbfSMike Smith 			pattern_buf = malloc(cio->pat_buf_len, M_TEMP,
280a163d034SWarner Losh 					     M_WAITOK);
2818983cfbfSMike Smith 			error = copyin(cio->patterns, pattern_buf,
2828983cfbfSMike Smith 				       cio->pat_buf_len);
283d08239c1SJohn-Mark Gurney 			if (error != 0) {
284d08239c1SJohn-Mark Gurney 				error = EINVAL;
285d08239c1SJohn-Mark Gurney 				goto getconfexit;
286d08239c1SJohn-Mark Gurney 			}
2878983cfbfSMike Smith 			num_patterns = cio->num_patterns;
2888983cfbfSMike Smith 
2898983cfbfSMike Smith 		} else if ((cio->num_patterns > 0)
2908983cfbfSMike Smith 			|| (cio->pat_buf_len > 0)) {
2918983cfbfSMike Smith 			/*
2928983cfbfSMike Smith 			 * The user made a mistake, spit out an error.
2938983cfbfSMike Smith 			 */
2948983cfbfSMike Smith 			cio->status = PCI_GETCONF_ERROR;
2958983cfbfSMike Smith 			cio->num_matches = 0;
2968983cfbfSMike Smith 			error = EINVAL;
2978983cfbfSMike Smith 			break;
2988983cfbfSMike Smith 		} else
2998983cfbfSMike Smith 			pattern_buf = NULL;
3008983cfbfSMike Smith 
3018983cfbfSMike Smith 		/*
3028983cfbfSMike Smith 		 * Go through the list of devices and copy out the devices
3038983cfbfSMike Smith 		 * that match the user's criteria.
3048983cfbfSMike Smith 		 */
3058983cfbfSMike Smith 		for (cio->num_matches = 0, error = 0, i = 0,
3068983cfbfSMike Smith 		     dinfo = STAILQ_FIRST(devlist_head);
3078983cfbfSMike Smith 		     (dinfo != NULL) && (cio->num_matches < ionum)
308d08239c1SJohn-Mark Gurney 		     && (error == 0) && (i < pci_numdevs) && (dinfo != NULL);
3098983cfbfSMike Smith 		     dinfo = STAILQ_NEXT(dinfo, pci_links), i++) {
3108983cfbfSMike Smith 
3118983cfbfSMike Smith 			if (i < cio->offset)
3128983cfbfSMike Smith 				continue;
3138983cfbfSMike Smith 
3148983cfbfSMike Smith 			/* Populate pd_name and pd_unit */
3158983cfbfSMike Smith 			name = NULL;
3168983cfbfSMike Smith 			if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0')
3178983cfbfSMike Smith 				name = device_get_name(dinfo->cfg.dev);
3188983cfbfSMike Smith 			if (name) {
3198983cfbfSMike Smith 				strncpy(dinfo->conf.pd_name, name,
3208983cfbfSMike Smith 					sizeof(dinfo->conf.pd_name));
3218983cfbfSMike Smith 				dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0;
3228983cfbfSMike Smith 				dinfo->conf.pd_unit =
3238983cfbfSMike Smith 					device_get_unit(dinfo->cfg.dev);
3248983cfbfSMike Smith 			}
3258983cfbfSMike Smith 
3268983cfbfSMike Smith 			if ((pattern_buf == NULL) ||
3278983cfbfSMike Smith 			    (pci_conf_match(pattern_buf, num_patterns,
3288983cfbfSMike Smith 					    &dinfo->conf) == 0)) {
3298983cfbfSMike Smith 
3308983cfbfSMike Smith 				/*
3318983cfbfSMike Smith 				 * If we've filled up the user's buffer,
3328983cfbfSMike Smith 				 * break out at this point.  Since we've
3338983cfbfSMike Smith 				 * got a match here, we'll pick right back
3348983cfbfSMike Smith 				 * up at the matching entry.  We can also
3358983cfbfSMike Smith 				 * tell the user that there are more matches
3368983cfbfSMike Smith 				 * left.
3378983cfbfSMike Smith 				 */
3388983cfbfSMike Smith 				if (cio->num_matches >= ionum)
3398983cfbfSMike Smith 					break;
3408983cfbfSMike Smith 
341d08239c1SJohn-Mark Gurney 				/* only if can copy it out do we count it */
342d08239c1SJohn-Mark Gurney 				if (!(error = copyout(&dinfo->conf,
3438983cfbfSMike Smith 				    &cio->matches[cio->num_matches],
344d08239c1SJohn-Mark Gurney 				    sizeof(struct pci_conf)))) {
3458983cfbfSMike Smith 					cio->num_matches++;
3468983cfbfSMike Smith 				}
3478983cfbfSMike Smith 			}
348d08239c1SJohn-Mark Gurney 		}
3498983cfbfSMike Smith 
3508983cfbfSMike Smith 		/*
3518983cfbfSMike Smith 		 * Set the pointer into the list, so if the user is getting
3528983cfbfSMike Smith 		 * n records at a time, where n < pci_numdevs,
3538983cfbfSMike Smith 		 */
3548983cfbfSMike Smith 		cio->offset = i;
3558983cfbfSMike Smith 
3568983cfbfSMike Smith 		/*
3578983cfbfSMike Smith 		 * Set the generation, the user will need this if they make
3588983cfbfSMike Smith 		 * another ioctl call with offset != 0.
3598983cfbfSMike Smith 		 */
3608983cfbfSMike Smith 		cio->generation = pci_generation;
3618983cfbfSMike Smith 
3628983cfbfSMike Smith 		/*
3638983cfbfSMike Smith 		 * If this is the last device, inform the user so he won't
3648983cfbfSMike Smith 		 * bother asking for more devices.  If dinfo isn't NULL, we
3658983cfbfSMike Smith 		 * know that there are more matches in the list because of
3668983cfbfSMike Smith 		 * the way the traversal is done.
3678983cfbfSMike Smith 		 */
3688983cfbfSMike Smith 		if (dinfo == NULL)
3698983cfbfSMike Smith 			cio->status = PCI_GETCONF_LAST_DEVICE;
3708983cfbfSMike Smith 		else
3718983cfbfSMike Smith 			cio->status = PCI_GETCONF_MORE_DEVS;
3728983cfbfSMike Smith 
373d08239c1SJohn-Mark Gurney getconfexit:
3748983cfbfSMike Smith 		if (pattern_buf != NULL)
3758983cfbfSMike Smith 			free(pattern_buf, M_TEMP);
3768983cfbfSMike Smith 
3778983cfbfSMike Smith 		break;
3788983cfbfSMike Smith 		}
3798983cfbfSMike Smith 	case PCIOCREAD:
3808983cfbfSMike Smith 		io = (struct pci_io *)data;
3818983cfbfSMike Smith 		switch(io->pi_width) {
3828983cfbfSMike Smith 		case 4:
3838983cfbfSMike Smith 		case 2:
3848983cfbfSMike Smith 		case 1:
3858983cfbfSMike Smith 			/*
3868983cfbfSMike Smith 			 * Assume that the user-level bus number is
3878983cfbfSMike Smith 			 * actually the pciN instance number. We map
3888983cfbfSMike Smith 			 * from that to the real pcib+bus combination.
3898983cfbfSMike Smith 			 */
3908983cfbfSMike Smith 			pci = devclass_get_device(devclass_find("pci"),
3918983cfbfSMike Smith 						  io->pi_sel.pc_bus);
3928983cfbfSMike Smith 			if (pci) {
3938983cfbfSMike Smith 				int b = pcib_get_bus(pci);
3948983cfbfSMike Smith 				pcib = device_get_parent(pci);
3958983cfbfSMike Smith 				io->pi_data =
3968983cfbfSMike Smith 					PCIB_READ_CONFIG(pcib,
3978983cfbfSMike Smith 							 b,
3988983cfbfSMike Smith 							 io->pi_sel.pc_dev,
3998983cfbfSMike Smith 							 io->pi_sel.pc_func,
4008983cfbfSMike Smith 							 io->pi_reg,
4018983cfbfSMike Smith 							 io->pi_width);
4028983cfbfSMike Smith 				error = 0;
4038983cfbfSMike Smith 			} else {
4048983cfbfSMike Smith 				error = ENODEV;
4058983cfbfSMike Smith 			}
4068983cfbfSMike Smith 			break;
4078983cfbfSMike Smith 		default:
408d08239c1SJohn-Mark Gurney 			error = EINVAL;
4098983cfbfSMike Smith 			break;
4108983cfbfSMike Smith 		}
4118983cfbfSMike Smith 		break;
4128983cfbfSMike Smith 
4138983cfbfSMike Smith 	case PCIOCWRITE:
4148983cfbfSMike Smith 		io = (struct pci_io *)data;
4158983cfbfSMike Smith 		switch(io->pi_width) {
4168983cfbfSMike Smith 		case 4:
4178983cfbfSMike Smith 		case 2:
4188983cfbfSMike Smith 		case 1:
4198983cfbfSMike Smith 			/*
4208983cfbfSMike Smith 			 * Assume that the user-level bus number is
4218983cfbfSMike Smith 			 * actually the pciN instance number. We map
4228983cfbfSMike Smith 			 * from that to the real pcib+bus combination.
4238983cfbfSMike Smith 			 */
4248983cfbfSMike Smith 			pci = devclass_get_device(devclass_find("pci"),
4258983cfbfSMike Smith 						  io->pi_sel.pc_bus);
4268983cfbfSMike Smith 			if (pci) {
4278983cfbfSMike Smith 				int b = pcib_get_bus(pci);
4288983cfbfSMike Smith 				pcib = device_get_parent(pci);
4298983cfbfSMike Smith 				PCIB_WRITE_CONFIG(pcib,
4308983cfbfSMike Smith 						  b,
4318983cfbfSMike Smith 						  io->pi_sel.pc_dev,
4328983cfbfSMike Smith 						  io->pi_sel.pc_func,
4338983cfbfSMike Smith 						  io->pi_reg,
4348983cfbfSMike Smith 						  io->pi_data,
4358983cfbfSMike Smith 						  io->pi_width);
4368983cfbfSMike Smith 				error = 0;
4378983cfbfSMike Smith 			} else {
4388983cfbfSMike Smith 				error = ENODEV;
4398983cfbfSMike Smith 			}
4408983cfbfSMike Smith 			break;
4418983cfbfSMike Smith 		default:
442d08239c1SJohn-Mark Gurney 			error = EINVAL;
4438983cfbfSMike Smith 			break;
4448983cfbfSMike Smith 		}
4458983cfbfSMike Smith 		break;
4468983cfbfSMike Smith 
4478983cfbfSMike Smith 	default:
4488983cfbfSMike Smith 		error = ENOTTY;
4498983cfbfSMike Smith 		break;
4508983cfbfSMike Smith 	}
4518983cfbfSMike Smith 
4528983cfbfSMike Smith 	return (error);
4538983cfbfSMike Smith }
4548983cfbfSMike Smith 
455