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 */ 1288983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 1298983cfbfSMike Smith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 1308983cfbfSMike Smith continue; 1318983cfbfSMike Smith 1328983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 1338983cfbfSMike Smith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 1348983cfbfSMike Smith continue; 1358983cfbfSMike Smith 1368983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 1378983cfbfSMike Smith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 1388983cfbfSMike Smith continue; 1398983cfbfSMike Smith 1408983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 1418983cfbfSMike Smith && (match_buf->pc_vendor != matches[i].pc_vendor)) 1428983cfbfSMike Smith continue; 1438983cfbfSMike Smith 1448983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 1458983cfbfSMike Smith && (match_buf->pc_device != matches[i].pc_device)) 1468983cfbfSMike Smith continue; 1478983cfbfSMike Smith 1488983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 1498983cfbfSMike Smith && (match_buf->pc_class != matches[i].pc_class)) 1508983cfbfSMike Smith continue; 1518983cfbfSMike Smith 1528983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 1538983cfbfSMike Smith && (match_buf->pd_unit != matches[i].pd_unit)) 1548983cfbfSMike Smith continue; 1558983cfbfSMike Smith 1568983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 1578983cfbfSMike Smith && (strncmp(matches[i].pd_name, match_buf->pd_name, 1588983cfbfSMike Smith sizeof(match_buf->pd_name)) != 0)) 1598983cfbfSMike Smith continue; 1608983cfbfSMike Smith 1618983cfbfSMike Smith return(0); 1628983cfbfSMike Smith } 1638983cfbfSMike Smith 1648983cfbfSMike Smith return(1); 1658983cfbfSMike Smith } 1668983cfbfSMike Smith 1678983cfbfSMike Smith static int 16889c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1698983cfbfSMike Smith { 17037ce43b7SBruce M Simpson device_t pcidev; 1718983cfbfSMike Smith struct pci_io *io; 1728983cfbfSMike Smith const char *name; 1738983cfbfSMike Smith int error; 1748983cfbfSMike Smith 175d08239c1SJohn-Mark Gurney if (!(flag & FWRITE) && cmd != PCIOCGETCONF) 1768983cfbfSMike Smith return EPERM; 1778983cfbfSMike Smith 1788983cfbfSMike Smith switch(cmd) { 1798983cfbfSMike Smith case PCIOCGETCONF: 1808983cfbfSMike Smith { 1818983cfbfSMike Smith struct pci_devinfo *dinfo; 1828983cfbfSMike Smith struct pci_conf_io *cio; 1838983cfbfSMike Smith struct devlist *devlist_head; 1848983cfbfSMike Smith struct pci_match_conf *pattern_buf; 1858983cfbfSMike Smith int num_patterns; 1868983cfbfSMike Smith size_t iolen; 1878983cfbfSMike Smith int ionum, i; 1888983cfbfSMike Smith 1898983cfbfSMike Smith cio = (struct pci_conf_io *)data; 1908983cfbfSMike Smith 1918983cfbfSMike Smith num_patterns = 0; 1928983cfbfSMike Smith dinfo = NULL; 1938983cfbfSMike Smith 1948983cfbfSMike Smith /* 1958983cfbfSMike Smith * If the user specified an offset into the device list, 1968983cfbfSMike Smith * but the list has changed since they last called this 1978983cfbfSMike Smith * ioctl, tell them that the list has changed. They will 1988983cfbfSMike Smith * have to get the list from the beginning. 1998983cfbfSMike Smith */ 2008983cfbfSMike Smith if ((cio->offset != 0) 2018983cfbfSMike Smith && (cio->generation != pci_generation)){ 2028983cfbfSMike Smith cio->num_matches = 0; 2038983cfbfSMike Smith cio->status = PCI_GETCONF_LIST_CHANGED; 2048983cfbfSMike Smith error = 0; 2058983cfbfSMike Smith break; 2068983cfbfSMike Smith } 2078983cfbfSMike Smith 2088983cfbfSMike Smith /* 2098983cfbfSMike Smith * Check to see whether the user has asked for an offset 2108983cfbfSMike Smith * past the end of our list. 2118983cfbfSMike Smith */ 2128983cfbfSMike Smith if (cio->offset >= pci_numdevs) { 2138983cfbfSMike Smith cio->num_matches = 0; 2148983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 2158983cfbfSMike Smith error = 0; 2168983cfbfSMike Smith break; 2178983cfbfSMike Smith } 2188983cfbfSMike Smith 2198983cfbfSMike Smith /* get the head of the device queue */ 2208983cfbfSMike Smith devlist_head = &pci_devq; 2218983cfbfSMike Smith 2228983cfbfSMike Smith /* 2238983cfbfSMike Smith * Determine how much room we have for pci_conf structures. 2248983cfbfSMike Smith * Round the user's buffer size down to the nearest 2258983cfbfSMike Smith * multiple of sizeof(struct pci_conf) in case the user 2268983cfbfSMike Smith * didn't specify a multiple of that size. 2278983cfbfSMike Smith */ 2288983cfbfSMike Smith iolen = min(cio->match_buf_len - 2298983cfbfSMike Smith (cio->match_buf_len % sizeof(struct pci_conf)), 2308983cfbfSMike Smith pci_numdevs * sizeof(struct pci_conf)); 2318983cfbfSMike Smith 2328983cfbfSMike Smith /* 2338983cfbfSMike Smith * Since we know that iolen is a multiple of the size of 2348983cfbfSMike Smith * the pciconf union, it's okay to do this. 2358983cfbfSMike Smith */ 2368983cfbfSMike Smith ionum = iolen / sizeof(struct pci_conf); 2378983cfbfSMike Smith 2388983cfbfSMike Smith /* 2398983cfbfSMike Smith * If this test is true, the user wants the pci_conf 2408983cfbfSMike Smith * structures returned to match the supplied entries. 2418983cfbfSMike Smith */ 242e3f932deSJohn-Mark Gurney if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 2438983cfbfSMike Smith && (cio->pat_buf_len > 0)) { 2448983cfbfSMike Smith /* 2458983cfbfSMike Smith * pat_buf_len needs to be: 2468983cfbfSMike Smith * num_patterns * sizeof(struct pci_match_conf) 2478983cfbfSMike Smith * While it is certainly possible the user just 2488983cfbfSMike Smith * allocated a large buffer, but set the number of 2498983cfbfSMike Smith * matches correctly, it is far more likely that 2508983cfbfSMike Smith * their kernel doesn't match the userland utility 2518983cfbfSMike Smith * they're using. It's also possible that the user 2528983cfbfSMike Smith * forgot to initialize some variables. Yes, this 2538983cfbfSMike Smith * may be overly picky, but I hazard to guess that 2548983cfbfSMike Smith * it's far more likely to just catch folks that 2558983cfbfSMike Smith * updated their kernel but not their userland. 2568983cfbfSMike Smith */ 2578983cfbfSMike Smith if ((cio->num_patterns * 2588983cfbfSMike Smith sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 2598983cfbfSMike Smith /* The user made a mistake, return an error*/ 2608983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2618983cfbfSMike Smith cio->num_matches = 0; 2628983cfbfSMike Smith error = EINVAL; 2638983cfbfSMike Smith break; 2648983cfbfSMike Smith } 2658983cfbfSMike Smith 2668983cfbfSMike Smith /* 2678983cfbfSMike Smith * Allocate a buffer to hold the patterns. 2688983cfbfSMike Smith */ 2698983cfbfSMike Smith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 270a163d034SWarner Losh M_WAITOK); 2718983cfbfSMike Smith error = copyin(cio->patterns, pattern_buf, 2728983cfbfSMike Smith cio->pat_buf_len); 273d08239c1SJohn-Mark Gurney if (error != 0) { 274d08239c1SJohn-Mark Gurney error = EINVAL; 275d08239c1SJohn-Mark Gurney goto getconfexit; 276d08239c1SJohn-Mark Gurney } 2778983cfbfSMike Smith num_patterns = cio->num_patterns; 2788983cfbfSMike Smith 2798983cfbfSMike Smith } else if ((cio->num_patterns > 0) 2808983cfbfSMike Smith || (cio->pat_buf_len > 0)) { 2818983cfbfSMike Smith /* 2828983cfbfSMike Smith * The user made a mistake, spit out an error. 2838983cfbfSMike Smith */ 2848983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2858983cfbfSMike Smith cio->num_matches = 0; 2868983cfbfSMike Smith error = EINVAL; 2878983cfbfSMike Smith break; 2888983cfbfSMike Smith } else 2898983cfbfSMike Smith pattern_buf = NULL; 2908983cfbfSMike Smith 2918983cfbfSMike Smith /* 2928983cfbfSMike Smith * Go through the list of devices and copy out the devices 2938983cfbfSMike Smith * that match the user's criteria. 2948983cfbfSMike Smith */ 2958983cfbfSMike Smith for (cio->num_matches = 0, error = 0, i = 0, 2968983cfbfSMike Smith dinfo = STAILQ_FIRST(devlist_head); 2978983cfbfSMike Smith (dinfo != NULL) && (cio->num_matches < ionum) 298d08239c1SJohn-Mark Gurney && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 2998983cfbfSMike Smith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 3008983cfbfSMike Smith 3018983cfbfSMike Smith if (i < cio->offset) 3028983cfbfSMike Smith continue; 3038983cfbfSMike Smith 3048983cfbfSMike Smith /* Populate pd_name and pd_unit */ 3058983cfbfSMike Smith name = NULL; 3068983cfbfSMike Smith if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 3078983cfbfSMike Smith name = device_get_name(dinfo->cfg.dev); 3088983cfbfSMike Smith if (name) { 3098983cfbfSMike Smith strncpy(dinfo->conf.pd_name, name, 3108983cfbfSMike Smith sizeof(dinfo->conf.pd_name)); 3118983cfbfSMike Smith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 3128983cfbfSMike Smith dinfo->conf.pd_unit = 3138983cfbfSMike Smith device_get_unit(dinfo->cfg.dev); 3148983cfbfSMike Smith } 3158983cfbfSMike Smith 3168983cfbfSMike Smith if ((pattern_buf == NULL) || 3178983cfbfSMike Smith (pci_conf_match(pattern_buf, num_patterns, 3188983cfbfSMike Smith &dinfo->conf) == 0)) { 3198983cfbfSMike Smith 3208983cfbfSMike Smith /* 3218983cfbfSMike Smith * If we've filled up the user's buffer, 3228983cfbfSMike Smith * break out at this point. Since we've 3238983cfbfSMike Smith * got a match here, we'll pick right back 3248983cfbfSMike Smith * up at the matching entry. We can also 3258983cfbfSMike Smith * tell the user that there are more matches 3268983cfbfSMike Smith * left. 3278983cfbfSMike Smith */ 3288983cfbfSMike Smith if (cio->num_matches >= ionum) 3298983cfbfSMike Smith break; 3308983cfbfSMike Smith 331d08239c1SJohn-Mark Gurney /* only if can copy it out do we count it */ 332d08239c1SJohn-Mark Gurney if (!(error = copyout(&dinfo->conf, 3338983cfbfSMike Smith &cio->matches[cio->num_matches], 334d08239c1SJohn-Mark Gurney sizeof(struct pci_conf)))) { 3358983cfbfSMike Smith cio->num_matches++; 3368983cfbfSMike Smith } 3378983cfbfSMike Smith } 338d08239c1SJohn-Mark Gurney } 3398983cfbfSMike Smith 3408983cfbfSMike Smith /* 3418983cfbfSMike Smith * Set the pointer into the list, so if the user is getting 3428983cfbfSMike Smith * n records at a time, where n < pci_numdevs, 3438983cfbfSMike Smith */ 3448983cfbfSMike Smith cio->offset = i; 3458983cfbfSMike Smith 3468983cfbfSMike Smith /* 3478983cfbfSMike Smith * Set the generation, the user will need this if they make 3488983cfbfSMike Smith * another ioctl call with offset != 0. 3498983cfbfSMike Smith */ 3508983cfbfSMike Smith cio->generation = pci_generation; 3518983cfbfSMike Smith 3528983cfbfSMike Smith /* 3538983cfbfSMike Smith * If this is the last device, inform the user so he won't 3548983cfbfSMike Smith * bother asking for more devices. If dinfo isn't NULL, we 3558983cfbfSMike Smith * know that there are more matches in the list because of 3568983cfbfSMike Smith * the way the traversal is done. 3578983cfbfSMike Smith */ 3588983cfbfSMike Smith if (dinfo == NULL) 3598983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 3608983cfbfSMike Smith else 3618983cfbfSMike Smith cio->status = PCI_GETCONF_MORE_DEVS; 3628983cfbfSMike Smith 363d08239c1SJohn-Mark Gurney getconfexit: 3648983cfbfSMike Smith if (pattern_buf != NULL) 3658983cfbfSMike Smith free(pattern_buf, M_TEMP); 3668983cfbfSMike Smith 3678983cfbfSMike Smith break; 3688983cfbfSMike Smith } 3698983cfbfSMike Smith 37066f314b5SStefan Eßer case PCIOCREAD: 3718983cfbfSMike Smith case PCIOCWRITE: 3728983cfbfSMike Smith io = (struct pci_io *)data; 3738983cfbfSMike Smith switch(io->pi_width) { 3748983cfbfSMike Smith case 4: 3758983cfbfSMike Smith case 2: 3768983cfbfSMike Smith case 1: 37766f314b5SStefan Eßer /* make sure register is in bounds and aligned */ 37866f314b5SStefan Eßer if (cmd == PCIOCREAD || cmd == PCIOCWRITE) 37966f314b5SStefan Eßer if (io->pi_reg < 0 || 38066f314b5SStefan Eßer io->pi_reg + io->pi_width > PCI_REGMAX || 38166f314b5SStefan Eßer io->pi_reg & (io->pi_width - 1)) 38266f314b5SStefan Eßer error = EINVAL; 3838983cfbfSMike Smith /* 3848983cfbfSMike Smith * Assume that the user-level bus number is 38537ce43b7SBruce M Simpson * in fact the physical PCI bus number. 38637ce43b7SBruce M Simpson * Look up the grandparent, i.e. the bridge device, 38737ce43b7SBruce M Simpson * so that we can issue configuration space cycles. 3888983cfbfSMike Smith */ 38937ce43b7SBruce M Simpson pcidev = pci_find_bsf(io->pi_sel.pc_bus, 39037ce43b7SBruce M Simpson io->pi_sel.pc_dev, io->pi_sel.pc_func); 39137ce43b7SBruce M Simpson if (pcidev) { 39237ce43b7SBruce M Simpson device_t busdev, brdev; 39337ce43b7SBruce M Simpson 39437ce43b7SBruce M Simpson busdev = device_get_parent(pcidev); 39537ce43b7SBruce M Simpson brdev = device_get_parent(busdev); 39637ce43b7SBruce M Simpson 39766f314b5SStefan Eßer if (cmd == PCIOCWRITE) 39837ce43b7SBruce M Simpson PCIB_WRITE_CONFIG(brdev, 39937ce43b7SBruce M Simpson io->pi_sel.pc_bus, 4008983cfbfSMike Smith io->pi_sel.pc_dev, 4018983cfbfSMike Smith io->pi_sel.pc_func, 4028983cfbfSMike Smith io->pi_reg, 4038983cfbfSMike Smith io->pi_data, 4048983cfbfSMike Smith io->pi_width); 40566f314b5SStefan Eßer else 40666f314b5SStefan Eßer io->pi_data = 40737ce43b7SBruce M Simpson PCIB_READ_CONFIG(brdev, 40837ce43b7SBruce M Simpson io->pi_sel.pc_bus, 40966f314b5SStefan Eßer io->pi_sel.pc_dev, 41066f314b5SStefan Eßer io->pi_sel.pc_func, 41166f314b5SStefan Eßer io->pi_reg, 41266f314b5SStefan Eßer io->pi_width); 4138983cfbfSMike Smith error = 0; 4148983cfbfSMike Smith } else { 4158910aa92SPaul Saab #ifdef COMPAT_FREEBSD4 4168910aa92SPaul Saab if (cmd == PCIOCREAD) { 4178910aa92SPaul Saab io->pi_data = -1; 4188910aa92SPaul Saab error = 0; 4198910aa92SPaul Saab } else 4208910aa92SPaul Saab #endif 4218983cfbfSMike Smith error = ENODEV; 4228983cfbfSMike Smith } 4238983cfbfSMike Smith break; 4248983cfbfSMike Smith default: 425d08239c1SJohn-Mark Gurney error = EINVAL; 4268983cfbfSMike Smith break; 4278983cfbfSMike Smith } 4288983cfbfSMike Smith break; 4298983cfbfSMike Smith 4308983cfbfSMike Smith default: 4318983cfbfSMike Smith error = ENOTTY; 4328983cfbfSMike Smith break; 4338983cfbfSMike Smith } 4348983cfbfSMike Smith 4358983cfbfSMike Smith return (error); 4368983cfbfSMike Smith } 437