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 */ 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 = { 77dc08ffecSPoul-Henning Kamp .d_version = D_VERSION, 78dc08ffecSPoul-Henning Kamp .d_flags = D_NEEDGIANT, 797ac40f5fSPoul-Henning Kamp .d_open = pci_open, 807ac40f5fSPoul-Henning Kamp .d_close = pci_close, 817ac40f5fSPoul-Henning Kamp .d_ioctl = pci_ioctl, 827ac40f5fSPoul-Henning Kamp .d_name = "pci", 837ac40f5fSPoul-Henning Kamp .d_maj = PCI_CDEV, 848983cfbfSMike Smith }; 858983cfbfSMike Smith 868983cfbfSMike Smith static int 8789c9c53dSPoul-Henning Kamp pci_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 888983cfbfSMike Smith { 898002488bSRobert Watson int error; 908002488bSRobert Watson 918002488bSRobert Watson if (oflags & FWRITE) { 92a854ed98SJohn Baldwin error = securelevel_gt(td->td_ucred, 0); 938002488bSRobert Watson if (error) 948002488bSRobert Watson return (error); 958983cfbfSMike Smith } 968002488bSRobert Watson 978002488bSRobert Watson return (0); 988983cfbfSMike Smith } 998983cfbfSMike Smith 1008983cfbfSMike Smith static int 10189c9c53dSPoul-Henning Kamp pci_close(struct cdev *dev, int flag, int devtype, struct thread *td) 1028983cfbfSMike Smith { 1038983cfbfSMike Smith return 0; 1048983cfbfSMike Smith } 1058983cfbfSMike Smith 1068983cfbfSMike Smith /* 1078983cfbfSMike Smith * Match a single pci_conf structure against an array of pci_match_conf 1088983cfbfSMike Smith * structures. The first argument, 'matches', is an array of num_matches 1098983cfbfSMike Smith * pci_match_conf structures. match_buf is a pointer to the pci_conf 1108983cfbfSMike Smith * structure that will be compared to every entry in the matches array. 1118983cfbfSMike Smith * This function returns 1 on failure, 0 on success. 1128983cfbfSMike Smith */ 1138983cfbfSMike Smith static int 1148983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches, 1158983cfbfSMike Smith struct pci_conf *match_buf) 1168983cfbfSMike Smith { 1178983cfbfSMike Smith int i; 1188983cfbfSMike Smith 1198983cfbfSMike Smith if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 1208983cfbfSMike Smith return(1); 1218983cfbfSMike Smith 1228983cfbfSMike Smith for (i = 0; i < num_matches; i++) { 1238983cfbfSMike Smith /* 1248983cfbfSMike Smith * I'm not sure why someone would do this...but... 1258983cfbfSMike Smith */ 1268983cfbfSMike Smith if (matches[i].flags == PCI_GETCONF_NO_MATCH) 1278983cfbfSMike Smith continue; 1288983cfbfSMike Smith 1298983cfbfSMike Smith /* 1308983cfbfSMike Smith * Look at each of the match flags. If it's set, do the 1318983cfbfSMike Smith * comparison. If the comparison fails, we don't have a 1328983cfbfSMike Smith * match, go on to the next item if there is one. 1338983cfbfSMike Smith */ 1348983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 1358983cfbfSMike Smith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 1368983cfbfSMike Smith continue; 1378983cfbfSMike Smith 1388983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 1398983cfbfSMike Smith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 1408983cfbfSMike Smith continue; 1418983cfbfSMike Smith 1428983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 1438983cfbfSMike Smith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 1448983cfbfSMike Smith continue; 1458983cfbfSMike Smith 1468983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 1478983cfbfSMike Smith && (match_buf->pc_vendor != matches[i].pc_vendor)) 1488983cfbfSMike Smith continue; 1498983cfbfSMike Smith 1508983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 1518983cfbfSMike Smith && (match_buf->pc_device != matches[i].pc_device)) 1528983cfbfSMike Smith continue; 1538983cfbfSMike Smith 1548983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 1558983cfbfSMike Smith && (match_buf->pc_class != matches[i].pc_class)) 1568983cfbfSMike Smith continue; 1578983cfbfSMike Smith 1588983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 1598983cfbfSMike Smith && (match_buf->pd_unit != matches[i].pd_unit)) 1608983cfbfSMike Smith continue; 1618983cfbfSMike Smith 1628983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 1638983cfbfSMike Smith && (strncmp(matches[i].pd_name, match_buf->pd_name, 1648983cfbfSMike Smith sizeof(match_buf->pd_name)) != 0)) 1658983cfbfSMike Smith continue; 1668983cfbfSMike Smith 1678983cfbfSMike Smith return(0); 1688983cfbfSMike Smith } 1698983cfbfSMike Smith 1708983cfbfSMike Smith return(1); 1718983cfbfSMike Smith } 1728983cfbfSMike Smith 1738983cfbfSMike Smith static int 17489c9c53dSPoul-Henning Kamp pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1758983cfbfSMike Smith { 1768983cfbfSMike Smith device_t pci, pcib; 1778983cfbfSMike Smith struct pci_io *io; 1788983cfbfSMike Smith const char *name; 1798983cfbfSMike Smith int error; 1808983cfbfSMike Smith 181d08239c1SJohn-Mark Gurney if (!(flag & FWRITE) && cmd != PCIOCGETCONF) 1828983cfbfSMike Smith return EPERM; 1838983cfbfSMike Smith 1848983cfbfSMike Smith switch(cmd) { 1858983cfbfSMike Smith case PCIOCGETCONF: 1868983cfbfSMike Smith { 1878983cfbfSMike Smith struct pci_devinfo *dinfo; 1888983cfbfSMike Smith struct pci_conf_io *cio; 1898983cfbfSMike Smith struct devlist *devlist_head; 1908983cfbfSMike Smith struct pci_match_conf *pattern_buf; 1918983cfbfSMike Smith int num_patterns; 1928983cfbfSMike Smith size_t iolen; 1938983cfbfSMike Smith int ionum, i; 1948983cfbfSMike Smith 1958983cfbfSMike Smith cio = (struct pci_conf_io *)data; 1968983cfbfSMike Smith 1978983cfbfSMike Smith num_patterns = 0; 1988983cfbfSMike Smith dinfo = NULL; 1998983cfbfSMike Smith 2008983cfbfSMike Smith /* 2018983cfbfSMike Smith * If the user specified an offset into the device list, 2028983cfbfSMike Smith * but the list has changed since they last called this 2038983cfbfSMike Smith * ioctl, tell them that the list has changed. They will 2048983cfbfSMike Smith * have to get the list from the beginning. 2058983cfbfSMike Smith */ 2068983cfbfSMike Smith if ((cio->offset != 0) 2078983cfbfSMike Smith && (cio->generation != pci_generation)){ 2088983cfbfSMike Smith cio->num_matches = 0; 2098983cfbfSMike Smith cio->status = PCI_GETCONF_LIST_CHANGED; 2108983cfbfSMike Smith error = 0; 2118983cfbfSMike Smith break; 2128983cfbfSMike Smith } 2138983cfbfSMike Smith 2148983cfbfSMike Smith /* 2158983cfbfSMike Smith * Check to see whether the user has asked for an offset 2168983cfbfSMike Smith * past the end of our list. 2178983cfbfSMike Smith */ 2188983cfbfSMike Smith if (cio->offset >= pci_numdevs) { 2198983cfbfSMike Smith cio->num_matches = 0; 2208983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 2218983cfbfSMike Smith error = 0; 2228983cfbfSMike Smith break; 2238983cfbfSMike Smith } 2248983cfbfSMike Smith 2258983cfbfSMike Smith /* get the head of the device queue */ 2268983cfbfSMike Smith devlist_head = &pci_devq; 2278983cfbfSMike Smith 2288983cfbfSMike Smith /* 2298983cfbfSMike Smith * Determine how much room we have for pci_conf structures. 2308983cfbfSMike Smith * Round the user's buffer size down to the nearest 2318983cfbfSMike Smith * multiple of sizeof(struct pci_conf) in case the user 2328983cfbfSMike Smith * didn't specify a multiple of that size. 2338983cfbfSMike Smith */ 2348983cfbfSMike Smith iolen = min(cio->match_buf_len - 2358983cfbfSMike Smith (cio->match_buf_len % sizeof(struct pci_conf)), 2368983cfbfSMike Smith pci_numdevs * sizeof(struct pci_conf)); 2378983cfbfSMike Smith 2388983cfbfSMike Smith /* 2398983cfbfSMike Smith * Since we know that iolen is a multiple of the size of 2408983cfbfSMike Smith * the pciconf union, it's okay to do this. 2418983cfbfSMike Smith */ 2428983cfbfSMike Smith ionum = iolen / sizeof(struct pci_conf); 2438983cfbfSMike Smith 2448983cfbfSMike Smith /* 2458983cfbfSMike Smith * If this test is true, the user wants the pci_conf 2468983cfbfSMike Smith * structures returned to match the supplied entries. 2478983cfbfSMike Smith */ 248e3f932deSJohn-Mark Gurney if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) 2498983cfbfSMike Smith && (cio->pat_buf_len > 0)) { 2508983cfbfSMike Smith /* 2518983cfbfSMike Smith * pat_buf_len needs to be: 2528983cfbfSMike Smith * num_patterns * sizeof(struct pci_match_conf) 2538983cfbfSMike Smith * While it is certainly possible the user just 2548983cfbfSMike Smith * allocated a large buffer, but set the number of 2558983cfbfSMike Smith * matches correctly, it is far more likely that 2568983cfbfSMike Smith * their kernel doesn't match the userland utility 2578983cfbfSMike Smith * they're using. It's also possible that the user 2588983cfbfSMike Smith * forgot to initialize some variables. Yes, this 2598983cfbfSMike Smith * may be overly picky, but I hazard to guess that 2608983cfbfSMike Smith * it's far more likely to just catch folks that 2618983cfbfSMike Smith * updated their kernel but not their userland. 2628983cfbfSMike Smith */ 2638983cfbfSMike Smith if ((cio->num_patterns * 2648983cfbfSMike Smith sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 2658983cfbfSMike Smith /* The user made a mistake, return an error*/ 2668983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2678983cfbfSMike Smith cio->num_matches = 0; 2688983cfbfSMike Smith error = EINVAL; 2698983cfbfSMike Smith break; 2708983cfbfSMike Smith } 2718983cfbfSMike Smith 2728983cfbfSMike Smith /* 2738983cfbfSMike Smith * Allocate a buffer to hold the patterns. 2748983cfbfSMike Smith */ 2758983cfbfSMike Smith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 276a163d034SWarner Losh M_WAITOK); 2778983cfbfSMike Smith error = copyin(cio->patterns, pattern_buf, 2788983cfbfSMike Smith cio->pat_buf_len); 279d08239c1SJohn-Mark Gurney if (error != 0) { 280d08239c1SJohn-Mark Gurney error = EINVAL; 281d08239c1SJohn-Mark Gurney goto getconfexit; 282d08239c1SJohn-Mark Gurney } 2838983cfbfSMike Smith num_patterns = cio->num_patterns; 2848983cfbfSMike Smith 2858983cfbfSMike Smith } else if ((cio->num_patterns > 0) 2868983cfbfSMike Smith || (cio->pat_buf_len > 0)) { 2878983cfbfSMike Smith /* 2888983cfbfSMike Smith * The user made a mistake, spit out an error. 2898983cfbfSMike Smith */ 2908983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2918983cfbfSMike Smith cio->num_matches = 0; 2928983cfbfSMike Smith error = EINVAL; 2938983cfbfSMike Smith break; 2948983cfbfSMike Smith } else 2958983cfbfSMike Smith pattern_buf = NULL; 2968983cfbfSMike Smith 2978983cfbfSMike Smith /* 2988983cfbfSMike Smith * Go through the list of devices and copy out the devices 2998983cfbfSMike Smith * that match the user's criteria. 3008983cfbfSMike Smith */ 3018983cfbfSMike Smith for (cio->num_matches = 0, error = 0, i = 0, 3028983cfbfSMike Smith dinfo = STAILQ_FIRST(devlist_head); 3038983cfbfSMike Smith (dinfo != NULL) && (cio->num_matches < ionum) 304d08239c1SJohn-Mark Gurney && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); 3058983cfbfSMike Smith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 3068983cfbfSMike Smith 3078983cfbfSMike Smith if (i < cio->offset) 3088983cfbfSMike Smith continue; 3098983cfbfSMike Smith 3108983cfbfSMike Smith /* Populate pd_name and pd_unit */ 3118983cfbfSMike Smith name = NULL; 3128983cfbfSMike Smith if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 3138983cfbfSMike Smith name = device_get_name(dinfo->cfg.dev); 3148983cfbfSMike Smith if (name) { 3158983cfbfSMike Smith strncpy(dinfo->conf.pd_name, name, 3168983cfbfSMike Smith sizeof(dinfo->conf.pd_name)); 3178983cfbfSMike Smith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 3188983cfbfSMike Smith dinfo->conf.pd_unit = 3198983cfbfSMike Smith device_get_unit(dinfo->cfg.dev); 3208983cfbfSMike Smith } 3218983cfbfSMike Smith 3228983cfbfSMike Smith if ((pattern_buf == NULL) || 3238983cfbfSMike Smith (pci_conf_match(pattern_buf, num_patterns, 3248983cfbfSMike Smith &dinfo->conf) == 0)) { 3258983cfbfSMike Smith 3268983cfbfSMike Smith /* 3278983cfbfSMike Smith * If we've filled up the user's buffer, 3288983cfbfSMike Smith * break out at this point. Since we've 3298983cfbfSMike Smith * got a match here, we'll pick right back 3308983cfbfSMike Smith * up at the matching entry. We can also 3318983cfbfSMike Smith * tell the user that there are more matches 3328983cfbfSMike Smith * left. 3338983cfbfSMike Smith */ 3348983cfbfSMike Smith if (cio->num_matches >= ionum) 3358983cfbfSMike Smith break; 3368983cfbfSMike Smith 337d08239c1SJohn-Mark Gurney /* only if can copy it out do we count it */ 338d08239c1SJohn-Mark Gurney if (!(error = copyout(&dinfo->conf, 3398983cfbfSMike Smith &cio->matches[cio->num_matches], 340d08239c1SJohn-Mark Gurney sizeof(struct pci_conf)))) { 3418983cfbfSMike Smith cio->num_matches++; 3428983cfbfSMike Smith } 3438983cfbfSMike Smith } 344d08239c1SJohn-Mark Gurney } 3458983cfbfSMike Smith 3468983cfbfSMike Smith /* 3478983cfbfSMike Smith * Set the pointer into the list, so if the user is getting 3488983cfbfSMike Smith * n records at a time, where n < pci_numdevs, 3498983cfbfSMike Smith */ 3508983cfbfSMike Smith cio->offset = i; 3518983cfbfSMike Smith 3528983cfbfSMike Smith /* 3538983cfbfSMike Smith * Set the generation, the user will need this if they make 3548983cfbfSMike Smith * another ioctl call with offset != 0. 3558983cfbfSMike Smith */ 3568983cfbfSMike Smith cio->generation = pci_generation; 3578983cfbfSMike Smith 3588983cfbfSMike Smith /* 3598983cfbfSMike Smith * If this is the last device, inform the user so he won't 3608983cfbfSMike Smith * bother asking for more devices. If dinfo isn't NULL, we 3618983cfbfSMike Smith * know that there are more matches in the list because of 3628983cfbfSMike Smith * the way the traversal is done. 3638983cfbfSMike Smith */ 3648983cfbfSMike Smith if (dinfo == NULL) 3658983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 3668983cfbfSMike Smith else 3678983cfbfSMike Smith cio->status = PCI_GETCONF_MORE_DEVS; 3688983cfbfSMike Smith 369d08239c1SJohn-Mark Gurney getconfexit: 3708983cfbfSMike Smith if (pattern_buf != NULL) 3718983cfbfSMike Smith free(pattern_buf, M_TEMP); 3728983cfbfSMike Smith 3738983cfbfSMike Smith break; 3748983cfbfSMike Smith } 3758983cfbfSMike Smith 37666f314b5SStefan Eßer case PCIOCREAD: 3778983cfbfSMike Smith case PCIOCWRITE: 3788983cfbfSMike Smith io = (struct pci_io *)data; 3798983cfbfSMike Smith switch(io->pi_width) { 3808983cfbfSMike Smith case 4: 3818983cfbfSMike Smith case 2: 3828983cfbfSMike Smith case 1: 38366f314b5SStefan Eßer /* make sure register is in bounds and aligned */ 38466f314b5SStefan Eßer if (cmd == PCIOCREAD || cmd == PCIOCWRITE) 38566f314b5SStefan Eßer if (io->pi_reg < 0 || 38666f314b5SStefan Eßer io->pi_reg + io->pi_width > PCI_REGMAX || 38766f314b5SStefan Eßer io->pi_reg & (io->pi_width - 1)) 38866f314b5SStefan Eßer error = EINVAL; 38966f314b5SStefan Eßer 3908983cfbfSMike Smith /* 3918983cfbfSMike Smith * Assume that the user-level bus number is 3928983cfbfSMike Smith * actually the pciN instance number. We map 3938983cfbfSMike Smith * from that to the real pcib+bus combination. 3948983cfbfSMike Smith */ 3958983cfbfSMike Smith pci = devclass_get_device(devclass_find("pci"), 3968983cfbfSMike Smith io->pi_sel.pc_bus); 3978983cfbfSMike Smith if (pci) { 3988983cfbfSMike Smith int b = pcib_get_bus(pci); 3998983cfbfSMike Smith pcib = device_get_parent(pci); 40066f314b5SStefan Eßer if (cmd == PCIOCWRITE) 4018983cfbfSMike Smith PCIB_WRITE_CONFIG(pcib, 4028983cfbfSMike Smith b, 4038983cfbfSMike Smith io->pi_sel.pc_dev, 4048983cfbfSMike Smith io->pi_sel.pc_func, 4058983cfbfSMike Smith io->pi_reg, 4068983cfbfSMike Smith io->pi_data, 4078983cfbfSMike Smith io->pi_width); 40866f314b5SStefan Eßer else 40966f314b5SStefan Eßer io->pi_data = 41066f314b5SStefan Eßer PCIB_READ_CONFIG(pcib, 41166f314b5SStefan Eßer b, 41266f314b5SStefan Eßer io->pi_sel.pc_dev, 41366f314b5SStefan Eßer io->pi_sel.pc_func, 41466f314b5SStefan Eßer io->pi_reg, 41566f314b5SStefan Eßer io->pi_width); 4168983cfbfSMike Smith error = 0; 4178983cfbfSMike Smith } else { 4188983cfbfSMike Smith error = ENODEV; 4198983cfbfSMike Smith } 4208983cfbfSMike Smith break; 4218983cfbfSMike Smith default: 422d08239c1SJohn-Mark Gurney error = EINVAL; 4238983cfbfSMike Smith break; 4248983cfbfSMike Smith } 4258983cfbfSMike Smith break; 4268983cfbfSMike Smith 4278983cfbfSMike Smith default: 4288983cfbfSMike Smith error = ENOTTY; 4298983cfbfSMike Smith break; 4308983cfbfSMike Smith } 4318983cfbfSMike Smith 4328983cfbfSMike Smith return (error); 4338983cfbfSMike Smith } 434