1aad970f1SDavid E. O'Brien /*- 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 27aad970f1SDavid E. O'Brien #include <sys/cdefs.h> 28aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$"); 29aad970f1SDavid E. O'Brien 308983cfbfSMike Smith #include "opt_bus.h" /* XXX trim includes */ 318910aa92SPaul Saab #include "opt_compat.h" 328983cfbfSMike Smith 338983cfbfSMike Smith #include <sys/param.h> 348983cfbfSMike Smith #include <sys/systm.h> 358983cfbfSMike Smith #include <sys/malloc.h> 368983cfbfSMike Smith #include <sys/module.h> 378983cfbfSMike Smith #include <sys/linker.h> 388983cfbfSMike Smith #include <sys/fcntl.h> 398983cfbfSMike Smith #include <sys/conf.h> 408983cfbfSMike Smith #include <sys/kernel.h> 418002488bSRobert Watson #include <sys/proc.h> 428983cfbfSMike Smith #include <sys/queue.h> 438983cfbfSMike Smith #include <sys/types.h> 448983cfbfSMike Smith 458983cfbfSMike Smith #include <vm/vm.h> 468983cfbfSMike Smith #include <vm/pmap.h> 478983cfbfSMike Smith #include <vm/vm_extern.h> 488983cfbfSMike Smith 498983cfbfSMike Smith #include <sys/bus.h> 508983cfbfSMike Smith #include <machine/bus.h> 518983cfbfSMike Smith #include <sys/rman.h> 528983cfbfSMike Smith #include <machine/resource.h> 538983cfbfSMike Smith 548983cfbfSMike Smith #include <sys/pciio.h> 5538d8c994SWarner Losh #include <dev/pci/pcireg.h> 5638d8c994SWarner Losh #include <dev/pci/pcivar.h> 578983cfbfSMike Smith 588983cfbfSMike Smith #include "pcib_if.h" 598983cfbfSMike Smith #include "pci_if.h" 608983cfbfSMike Smith 618983cfbfSMike Smith /* 628983cfbfSMike Smith * This is the user interface to PCI configuration space. 638983cfbfSMike Smith */ 648983cfbfSMike Smith 65b40ce416SJulian Elischer static d_open_t pci_open; 66b40ce416SJulian Elischer static d_close_t pci_close; 678983cfbfSMike Smith static int pci_conf_match(struct pci_match_conf *matches, int num_matches, 688983cfbfSMike Smith struct pci_conf *match_buf); 69b40ce416SJulian Elischer static d_ioctl_t pci_ioctl; 708983cfbfSMike Smith 718983cfbfSMike Smith struct cdevsw pcicdev = { 72dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 73dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 747ac40f5fSPoul-Henning Kamp .d_open = pci_open, 757ac40f5fSPoul-Henning Kamp .d_close = pci_close, 767ac40f5fSPoul-Henning Kamp .d_ioctl = pci_ioctl, 777ac40f5fSPoul-Henning Kamp .d_name = "pci", 788983cfbfSMike Smith }; 798983cfbfSMike Smith 808983cfbfSMike Smith static int 8189c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 828983cfbfSMike Smith { 838002488bSRobert Watson int error; 848002488bSRobert Watson 858002488bSRobert Watson if (oflags & FWRITE) { 86a854ed98SJohn Baldwin error = securelevel_gt(td->td_ucred, 0); 878002488bSRobert Watson if (error) 888002488bSRobert Watson return (error); 898983cfbfSMike Smith } 908002488bSRobert Watson 918002488bSRobert Watson return (0); 928983cfbfSMike Smith } 938983cfbfSMike Smith 948983cfbfSMike Smith static int 9589c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 968983cfbfSMike Smith { 978983cfbfSMike Smith return 0; 988983cfbfSMike Smith } 998983cfbfSMike Smith 1008983cfbfSMike Smith /* 1018983cfbfSMike Smith * Match a single pci_conf structure against an array of pci_match_conf 1028983cfbfSMike Smith * structures. The first argument, 'matches', is an array of num_matches 1038983cfbfSMike Smith * pci_match_conf structures. match_buf is a pointer to the pci_conf 1048983cfbfSMike Smith * structure that will be compared to every entry in the matches array. 1058983cfbfSMike Smith * This function returns 1 on failure, 0 on success. 1068983cfbfSMike Smith */ 1078983cfbfSMike Smith static int 1088983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches, 1098983cfbfSMike Smith struct pci_conf *match_buf) 1108983cfbfSMike Smith { 1118983cfbfSMike Smith int i; 1128983cfbfSMike Smith 1138983cfbfSMike Smith if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 1148983cfbfSMike Smith return(1); 1158983cfbfSMike Smith 1168983cfbfSMike Smith for (i = 0; i < num_matches; i++) { 1178983cfbfSMike Smith /* 1188983cfbfSMike Smith * I'm not sure why someone would do this...but... 1198983cfbfSMike Smith */ 1208983cfbfSMike Smith if (matches[i].flags == PCI_GETCONF_NO_MATCH) 1218983cfbfSMike Smith continue; 1228983cfbfSMike Smith 1238983cfbfSMike Smith /* 1248983cfbfSMike Smith * Look at each of the match flags. If it's set, do the 1258983cfbfSMike Smith * comparison. If the comparison fails, we don't have a 1268983cfbfSMike Smith * match, go on to the next item if there is one. 1278983cfbfSMike Smith */ 12855aaf894SMarius Strobl if (((matches[i].flags & PCI_GETCONF_MATCH_DOMAIN) != 0) 12955aaf894SMarius Strobl && (match_buf->pc_sel.pc_domain != 13055aaf894SMarius Strobl matches[i].pc_sel.pc_domain)) 13155aaf894SMarius Strobl continue; 13255aaf894SMarius Strobl 1338983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 1348983cfbfSMike Smith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 1358983cfbfSMike Smith continue; 1368983cfbfSMike Smith 1378983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 1388983cfbfSMike Smith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 1398983cfbfSMike Smith continue; 1408983cfbfSMike Smith 1418983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 1428983cfbfSMike Smith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 1438983cfbfSMike Smith continue; 1448983cfbfSMike Smith 1458983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 1468983cfbfSMike Smith && (match_buf->pc_vendor != matches[i].pc_vendor)) 1478983cfbfSMike Smith continue; 1488983cfbfSMike Smith 1498983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 1508983cfbfSMike Smith && (match_buf->pc_device != matches[i].pc_device)) 1518983cfbfSMike Smith continue; 1528983cfbfSMike Smith 1538983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 1548983cfbfSMike Smith && (match_buf->pc_class != matches[i].pc_class)) 1558983cfbfSMike Smith continue; 1568983cfbfSMike Smith 1578983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 1588983cfbfSMike Smith && (match_buf->pd_unit != matches[i].pd_unit)) 1598983cfbfSMike Smith continue; 1608983cfbfSMike Smith 1618983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 1628983cfbfSMike Smith && (strncmp(matches[i].pd_name, match_buf->pd_name, 1638983cfbfSMike Smith sizeof(match_buf->pd_name)) != 0)) 1648983cfbfSMike Smith continue; 1658983cfbfSMike Smith 1668983cfbfSMike Smith return(0); 1678983cfbfSMike Smith } 1688983cfbfSMike Smith 1698983cfbfSMike Smith return(1); 1708983cfbfSMike Smith } 1718983cfbfSMike Smith 1728983cfbfSMike Smith static int 17389c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1748983cfbfSMike Smith { 17537ce43b7SBruce M Simpson device_t pcidev; 1768983cfbfSMike Smith struct pci_io *io; 1778983cfbfSMike Smith const char *name; 1788983cfbfSMike Smith int error; 1798983cfbfSMike Smith 180d08239c1SJohn-Mark Gurney if (!(flag & FWRITE) && cmd != PCIOCGETCONF) 1818983cfbfSMike Smith return EPERM; 1828983cfbfSMike Smith 1838983cfbfSMike Smith switch(cmd) { 1848983cfbfSMike Smith case PCIOCGETCONF: 1858983cfbfSMike Smith { 1868983cfbfSMike Smith struct pci_devinfo *dinfo; 1878983cfbfSMike Smith struct pci_conf_io *cio; 1888983cfbfSMike Smith struct devlist *devlist_head; 1898983cfbfSMike Smith struct pci_match_conf *pattern_buf; 1908983cfbfSMike Smith int num_patterns; 1918983cfbfSMike Smith size_t iolen; 1928983cfbfSMike Smith int ionum, i; 1938983cfbfSMike Smith 1948983cfbfSMike Smith cio = (struct pci_conf_io *)data; 1958983cfbfSMike Smith 1968983cfbfSMike Smith num_patterns = 0; 1978983cfbfSMike Smith dinfo = NULL; 1988983cfbfSMike Smith 1998983cfbfSMike Smith /* 2008983cfbfSMike Smith * If the user specified an offset into the device list, 2018983cfbfSMike Smith * but the list has changed since they last called this 2028983cfbfSMike Smith * ioctl, tell them that the list has changed. They will 2038983cfbfSMike Smith * have to get the list from the beginning. 2048983cfbfSMike Smith */ 2058983cfbfSMike Smith if ((cio->offset != 0) 2068983cfbfSMike Smith && (cio->generation != pci_generation)){ 2078983cfbfSMike Smith cio->num_matches = 0; 2088983cfbfSMike Smith cio->status = PCI_GETCONF_LIST_CHANGED; 2098983cfbfSMike Smith error = 0; 2108983cfbfSMike Smith break; 2118983cfbfSMike Smith } 2128983cfbfSMike Smith 2138983cfbfSMike Smith /* 2148983cfbfSMike Smith * Check to see whether the user has asked for an offset 2158983cfbfSMike Smith * past the end of our list. 2168983cfbfSMike Smith */ 2178983cfbfSMike Smith if (cio->offset >= pci_numdevs) { 2188983cfbfSMike Smith cio->num_matches = 0; 2198983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 2208983cfbfSMike Smith error = 0; 2218983cfbfSMike Smith break; 2228983cfbfSMike Smith } 2238983cfbfSMike Smith 2248983cfbfSMike Smith /* get the head of the device queue */ 2258983cfbfSMike Smith devlist_head = &pci_devq; 2268983cfbfSMike Smith 2278983cfbfSMike Smith /* 2288983cfbfSMike Smith * Determine how much room we have for pci_conf structures. 2298983cfbfSMike Smith * Round the user's buffer size down to the nearest 2308983cfbfSMike Smith * multiple of sizeof(struct pci_conf) in case the user 2318983cfbfSMike Smith * didn't specify a multiple of that size. 2328983cfbfSMike Smith */ 2338983cfbfSMike Smith iolen = min(cio->match_buf_len - 2348983cfbfSMike Smith (cio->match_buf_len % sizeof(struct pci_conf)), 2358983cfbfSMike Smith pci_numdevs * sizeof(struct pci_conf)); 2368983cfbfSMike Smith 2378983cfbfSMike Smith /* 2388983cfbfSMike Smith * Since we know that iolen is a multiple of the size of 2398983cfbfSMike Smith * the pciconf union, it's okay to do this. 2408983cfbfSMike Smith */ 2418983cfbfSMike Smith ionum = iolen / sizeof(struct pci_conf); 2428983cfbfSMike Smith 2438983cfbfSMike Smith /* 2448983cfbfSMike Smith * If this test is true, the user wants the pci_conf 2458983cfbfSMike Smith * structures returned to match the supplied entries. 2468983cfbfSMike Smith */ 247e3f932deSJohn-Mark Gurney if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 2488983cfbfSMike Smith && (cio->pat_buf_len > 0)) { 2498983cfbfSMike Smith /* 2508983cfbfSMike Smith * pat_buf_len needs to be: 2518983cfbfSMike Smith * num_patterns * sizeof(struct pci_match_conf) 2528983cfbfSMike Smith * While it is certainly possible the user just 2538983cfbfSMike Smith * allocated a large buffer, but set the number of 2548983cfbfSMike Smith * matches correctly, it is far more likely that 2558983cfbfSMike Smith * their kernel doesn't match the userland utility 2568983cfbfSMike Smith * they're using. It's also possible that the user 2578983cfbfSMike Smith * forgot to initialize some variables. Yes, this 2588983cfbfSMike Smith * may be overly picky, but I hazard to guess that 2598983cfbfSMike Smith * it's far more likely to just catch folks that 2608983cfbfSMike Smith * updated their kernel but not their userland. 2618983cfbfSMike Smith */ 2628983cfbfSMike Smith if ((cio->num_patterns * 2638983cfbfSMike Smith sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 2648983cfbfSMike Smith /* The user made a mistake, return an error*/ 2658983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2668983cfbfSMike Smith cio->num_matches = 0; 2678983cfbfSMike Smith error = EINVAL; 2688983cfbfSMike Smith break; 2698983cfbfSMike Smith } 2708983cfbfSMike Smith 2718983cfbfSMike Smith /* 2728983cfbfSMike Smith * Allocate a buffer to hold the patterns. 2738983cfbfSMike Smith */ 2748983cfbfSMike Smith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 275a163d034SWarner Losh M_WAITOK); 2768983cfbfSMike Smith error = copyin(cio->patterns, pattern_buf, 2778983cfbfSMike Smith cio->pat_buf_len); 278d08239c1SJohn-Mark Gurney if (error != 0) { 279d08239c1SJohn-Mark Gurney error = EINVAL; 280d08239c1SJohn-Mark Gurney goto getconfexit; 281d08239c1SJohn-Mark Gurney } 2828983cfbfSMike Smith num_patterns = cio->num_patterns; 2838983cfbfSMike Smith 2848983cfbfSMike Smith } else if ((cio->num_patterns > 0) 2858983cfbfSMike Smith || (cio->pat_buf_len > 0)) { 2868983cfbfSMike Smith /* 2878983cfbfSMike Smith * The user made a mistake, spit out an error. 2888983cfbfSMike Smith */ 2898983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2908983cfbfSMike Smith cio->num_matches = 0; 2918983cfbfSMike Smith error = EINVAL; 2928983cfbfSMike Smith break; 2938983cfbfSMike Smith } else 2948983cfbfSMike Smith pattern_buf = NULL; 2958983cfbfSMike Smith 2968983cfbfSMike Smith /* 2978983cfbfSMike Smith * Go through the list of devices and copy out the devices 2988983cfbfSMike Smith * that match the user's criteria. 2998983cfbfSMike Smith */ 3008983cfbfSMike Smith for (cio->num_matches = 0, error = 0, i = 0, 3018983cfbfSMike Smith dinfo = STAILQ_FIRST(devlist_head); 3028983cfbfSMike Smith (dinfo != NULL) && (cio->num_matches < ionum) 303d08239c1SJohn-Mark Gurney && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 3048983cfbfSMike Smith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 3058983cfbfSMike Smith 3068983cfbfSMike Smith if (i < cio->offset) 3078983cfbfSMike Smith continue; 3088983cfbfSMike Smith 3098983cfbfSMike Smith /* Populate pd_name and pd_unit */ 3108983cfbfSMike Smith name = NULL; 3118983cfbfSMike Smith if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 3128983cfbfSMike Smith name = device_get_name(dinfo->cfg.dev); 3138983cfbfSMike Smith if (name) { 3148983cfbfSMike Smith strncpy(dinfo->conf.pd_name, name, 3158983cfbfSMike Smith sizeof(dinfo->conf.pd_name)); 3168983cfbfSMike Smith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 3178983cfbfSMike Smith dinfo->conf.pd_unit = 3188983cfbfSMike Smith device_get_unit(dinfo->cfg.dev); 3198983cfbfSMike Smith } 3208983cfbfSMike Smith 3218983cfbfSMike Smith if ((pattern_buf == NULL) || 3228983cfbfSMike Smith (pci_conf_match(pattern_buf, num_patterns, 3238983cfbfSMike Smith &dinfo->conf) == 0)) { 3248983cfbfSMike Smith 3258983cfbfSMike Smith /* 3268983cfbfSMike Smith * If we've filled up the user's buffer, 3278983cfbfSMike Smith * break out at this point. Since we've 3288983cfbfSMike Smith * got a match here, we'll pick right back 3298983cfbfSMike Smith * up at the matching entry. We can also 3308983cfbfSMike Smith * tell the user that there are more matches 3318983cfbfSMike Smith * left. 3328983cfbfSMike Smith */ 3338983cfbfSMike Smith if (cio->num_matches >= ionum) 3348983cfbfSMike Smith break; 3358983cfbfSMike Smith 336d08239c1SJohn-Mark Gurney /* only if can copy it out do we count it */ 337d08239c1SJohn-Mark Gurney if (!(error = copyout(&dinfo->conf, 3388983cfbfSMike Smith &cio->matches[cio->num_matches], 339d08239c1SJohn-Mark Gurney sizeof(struct pci_conf)))) { 3408983cfbfSMike Smith cio->num_matches++; 3418983cfbfSMike Smith } 3428983cfbfSMike Smith } 343d08239c1SJohn-Mark Gurney } 3448983cfbfSMike Smith 3458983cfbfSMike Smith /* 3468983cfbfSMike Smith * Set the pointer into the list, so if the user is getting 3478983cfbfSMike Smith * n records at a time, where n < pci_numdevs, 3488983cfbfSMike Smith */ 3498983cfbfSMike Smith cio->offset = i; 3508983cfbfSMike Smith 3518983cfbfSMike Smith /* 3528983cfbfSMike Smith * Set the generation, the user will need this if they make 3538983cfbfSMike Smith * another ioctl call with offset != 0. 3548983cfbfSMike Smith */ 3558983cfbfSMike Smith cio->generation = pci_generation; 3568983cfbfSMike Smith 3578983cfbfSMike Smith /* 3588983cfbfSMike Smith * If this is the last device, inform the user so he won't 3598983cfbfSMike Smith * bother asking for more devices. If dinfo isn't NULL, we 3608983cfbfSMike Smith * know that there are more matches in the list because of 3618983cfbfSMike Smith * the way the traversal is done. 3628983cfbfSMike Smith */ 3638983cfbfSMike Smith if (dinfo == NULL) 3648983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 3658983cfbfSMike Smith else 3668983cfbfSMike Smith cio->status = PCI_GETCONF_MORE_DEVS; 3678983cfbfSMike Smith 368d08239c1SJohn-Mark Gurney getconfexit: 3698983cfbfSMike Smith if (pattern_buf != NULL) 3708983cfbfSMike Smith free(pattern_buf, M_TEMP); 3718983cfbfSMike Smith 3728983cfbfSMike Smith break; 3738983cfbfSMike Smith } 3748983cfbfSMike Smith 37566f314b5SStefan Eßer case PCIOCREAD: 3768983cfbfSMike Smith case PCIOCWRITE: 3778983cfbfSMike Smith io = (struct pci_io *)data; 3788983cfbfSMike Smith switch(io->pi_width) { 3798983cfbfSMike Smith case 4: 3808983cfbfSMike Smith case 2: 3818983cfbfSMike Smith case 1: 38240ed3f47SRuslan Ermilov /* Make sure register is in bounds and aligned. */ 38340ed3f47SRuslan Ermilov if ((cmd == PCIOCREAD || cmd == PCIOCWRITE) && 38440ed3f47SRuslan Ermilov (io->pi_reg < 0 || 38540ed3f47SRuslan Ermilov io->pi_reg + io->pi_width > PCI_REGMAX + 1 || 38640ed3f47SRuslan Ermilov io->pi_reg & (io->pi_width - 1))) { 38766f314b5SStefan Eßer error = EINVAL; 38840ed3f47SRuslan Ermilov break; 38940ed3f47SRuslan Ermilov } 3908983cfbfSMike Smith /* 3918983cfbfSMike Smith * Assume that the user-level bus number is 39237ce43b7SBruce M Simpson * in fact the physical PCI bus number. 39337ce43b7SBruce M Simpson * Look up the grandparent, i.e. the bridge device, 39437ce43b7SBruce M Simpson * so that we can issue configuration space cycles. 3958983cfbfSMike Smith */ 39655aaf894SMarius Strobl pcidev = pci_find_dbsf(io->pi_sel.pc_domain, 39755aaf894SMarius Strobl io->pi_sel.pc_bus, io->pi_sel.pc_dev, 39855aaf894SMarius Strobl io->pi_sel.pc_func); 39937ce43b7SBruce M Simpson if (pcidev) { 40037ce43b7SBruce M Simpson device_t busdev, brdev; 40137ce43b7SBruce M Simpson 40237ce43b7SBruce M Simpson busdev = device_get_parent(pcidev); 40337ce43b7SBruce M Simpson brdev = device_get_parent(busdev); 40437ce43b7SBruce M Simpson 40566f314b5SStefan Eßer if (cmd == PCIOCWRITE) 40637ce43b7SBruce M Simpson PCIB_WRITE_CONFIG(brdev, 40737ce43b7SBruce M Simpson io->pi_sel.pc_bus, 4088983cfbfSMike Smith io->pi_sel.pc_dev, 4098983cfbfSMike Smith io->pi_sel.pc_func, 4108983cfbfSMike Smith io->pi_reg, 4118983cfbfSMike Smith io->pi_data, 4128983cfbfSMike Smith io->pi_width); 41366f314b5SStefan Eßer else 41466f314b5SStefan Eßer io->pi_data = 41537ce43b7SBruce M Simpson PCIB_READ_CONFIG(brdev, 41637ce43b7SBruce M Simpson io->pi_sel.pc_bus, 41766f314b5SStefan Eßer io->pi_sel.pc_dev, 41866f314b5SStefan Eßer io->pi_sel.pc_func, 41966f314b5SStefan Eßer io->pi_reg, 42066f314b5SStefan Eßer io->pi_width); 4218983cfbfSMike Smith error = 0; 4228983cfbfSMike Smith } else { 4238910aa92SPaul Saab #ifdef COMPAT_FREEBSD4 4248910aa92SPaul Saab if (cmd == PCIOCREAD) { 4258910aa92SPaul Saab io->pi_data = -1; 4268910aa92SPaul Saab error = 0; 4278910aa92SPaul Saab } else 4288910aa92SPaul Saab #endif 4298983cfbfSMike Smith error = ENODEV; 4308983cfbfSMike Smith } 4318983cfbfSMike Smith break; 4328983cfbfSMike Smith default: 433d08239c1SJohn-Mark Gurney error = EINVAL; 4348983cfbfSMike Smith break; 4358983cfbfSMike Smith } 4368983cfbfSMike Smith break; 4378983cfbfSMike Smith 4388983cfbfSMike Smith default: 4398983cfbfSMike Smith error = ENOTTY; 4408983cfbfSMike Smith break; 4418983cfbfSMike Smith } 4428983cfbfSMike Smith 4438983cfbfSMike Smith return (error); 4448983cfbfSMike Smith } 445