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> 548983cfbfSMike Smith #include <pci/pcireg.h> 558983cfbfSMike Smith #include <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 708983cfbfSMike Smith #define PCI_CDEV 78 718983cfbfSMike Smith 728983cfbfSMike Smith struct cdevsw pcicdev = { 738983cfbfSMike Smith /* open */ pci_open, 748983cfbfSMike Smith /* close */ pci_close, 758983cfbfSMike Smith /* read */ noread, 768983cfbfSMike Smith /* write */ nowrite, 778983cfbfSMike Smith /* ioctl */ pci_ioctl, 788983cfbfSMike Smith /* poll */ nopoll, 798983cfbfSMike Smith /* mmap */ nommap, 808983cfbfSMike Smith /* strategy */ nostrategy, 818983cfbfSMike Smith /* name */ "pci", 828983cfbfSMike Smith /* maj */ PCI_CDEV, 838983cfbfSMike Smith /* dump */ nodump, 848983cfbfSMike Smith /* psize */ nopsize, 858983cfbfSMike Smith /* flags */ 0, 868983cfbfSMike Smith }; 878983cfbfSMike Smith 888983cfbfSMike Smith static int 89b40ce416SJulian Elischer pci_open(dev_t dev, int oflags, int devtype, struct thread *td) 908983cfbfSMike Smith { 918002488bSRobert Watson int error; 928002488bSRobert Watson 938002488bSRobert Watson if (oflags & FWRITE) { 948002488bSRobert Watson error = securelevel_gt(td->td_proc->p_ucred, 0); 958002488bSRobert Watson if (error) 968002488bSRobert Watson return (error); 978983cfbfSMike Smith } 988002488bSRobert Watson 998002488bSRobert Watson return (0); 1008983cfbfSMike Smith } 1018983cfbfSMike Smith 1028983cfbfSMike Smith static int 103b40ce416SJulian Elischer pci_close(dev_t dev, int flag, int devtype, struct thread *td) 1048983cfbfSMike Smith { 1058983cfbfSMike Smith return 0; 1068983cfbfSMike Smith } 1078983cfbfSMike Smith 1088983cfbfSMike Smith /* 1098983cfbfSMike Smith * Match a single pci_conf structure against an array of pci_match_conf 1108983cfbfSMike Smith * structures. The first argument, 'matches', is an array of num_matches 1118983cfbfSMike Smith * pci_match_conf structures. match_buf is a pointer to the pci_conf 1128983cfbfSMike Smith * structure that will be compared to every entry in the matches array. 1138983cfbfSMike Smith * This function returns 1 on failure, 0 on success. 1148983cfbfSMike Smith */ 1158983cfbfSMike Smith static int 1168983cfbfSMike Smith pci_conf_match(struct pci_match_conf *matches, int num_matches, 1178983cfbfSMike Smith struct pci_conf *match_buf) 1188983cfbfSMike Smith { 1198983cfbfSMike Smith int i; 1208983cfbfSMike Smith 1218983cfbfSMike Smith if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) 1228983cfbfSMike Smith return(1); 1238983cfbfSMike Smith 1248983cfbfSMike Smith for (i = 0; i < num_matches; i++) { 1258983cfbfSMike Smith /* 1268983cfbfSMike Smith * I'm not sure why someone would do this...but... 1278983cfbfSMike Smith */ 1288983cfbfSMike Smith if (matches[i].flags == PCI_GETCONF_NO_MATCH) 1298983cfbfSMike Smith continue; 1308983cfbfSMike Smith 1318983cfbfSMike Smith /* 1328983cfbfSMike Smith * Look at each of the match flags. If it's set, do the 1338983cfbfSMike Smith * comparison. If the comparison fails, we don't have a 1348983cfbfSMike Smith * match, go on to the next item if there is one. 1358983cfbfSMike Smith */ 1368983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) 1378983cfbfSMike Smith && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) 1388983cfbfSMike Smith continue; 1398983cfbfSMike Smith 1408983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) 1418983cfbfSMike Smith && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) 1428983cfbfSMike Smith continue; 1438983cfbfSMike Smith 1448983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) 1458983cfbfSMike Smith && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) 1468983cfbfSMike Smith continue; 1478983cfbfSMike Smith 1488983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) 1498983cfbfSMike Smith && (match_buf->pc_vendor != matches[i].pc_vendor)) 1508983cfbfSMike Smith continue; 1518983cfbfSMike Smith 1528983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) 1538983cfbfSMike Smith && (match_buf->pc_device != matches[i].pc_device)) 1548983cfbfSMike Smith continue; 1558983cfbfSMike Smith 1568983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) 1578983cfbfSMike Smith && (match_buf->pc_class != matches[i].pc_class)) 1588983cfbfSMike Smith continue; 1598983cfbfSMike Smith 1608983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) 1618983cfbfSMike Smith && (match_buf->pd_unit != matches[i].pd_unit)) 1628983cfbfSMike Smith continue; 1638983cfbfSMike Smith 1648983cfbfSMike Smith if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) 1658983cfbfSMike Smith && (strncmp(matches[i].pd_name, match_buf->pd_name, 1668983cfbfSMike Smith sizeof(match_buf->pd_name)) != 0)) 1678983cfbfSMike Smith continue; 1688983cfbfSMike Smith 1698983cfbfSMike Smith return(0); 1708983cfbfSMike Smith } 1718983cfbfSMike Smith 1728983cfbfSMike Smith return(1); 1738983cfbfSMike Smith } 1748983cfbfSMike Smith 1758983cfbfSMike Smith static int 176b40ce416SJulian Elischer pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 1778983cfbfSMike Smith { 1788983cfbfSMike Smith device_t pci, pcib; 1798983cfbfSMike Smith struct pci_io *io; 1808983cfbfSMike Smith const char *name; 1818983cfbfSMike Smith int error; 1828983cfbfSMike Smith 1838983cfbfSMike Smith if (!(flag & FWRITE)) 1848983cfbfSMike Smith return EPERM; 1858983cfbfSMike Smith 1868983cfbfSMike Smith 1878983cfbfSMike Smith switch(cmd) { 1888983cfbfSMike Smith case PCIOCGETCONF: 1898983cfbfSMike Smith { 1908983cfbfSMike Smith struct pci_devinfo *dinfo; 1918983cfbfSMike Smith struct pci_conf_io *cio; 1928983cfbfSMike Smith struct devlist *devlist_head; 1938983cfbfSMike Smith struct pci_match_conf *pattern_buf; 1948983cfbfSMike Smith int num_patterns; 1958983cfbfSMike Smith size_t iolen; 1968983cfbfSMike Smith int ionum, i; 1978983cfbfSMike Smith 1988983cfbfSMike Smith cio = (struct pci_conf_io *)data; 1998983cfbfSMike Smith 2008983cfbfSMike Smith num_patterns = 0; 2018983cfbfSMike Smith dinfo = NULL; 2028983cfbfSMike Smith 2038983cfbfSMike Smith /* 2048983cfbfSMike Smith * Hopefully the user won't pass in a null pointer, but it 2058983cfbfSMike Smith * can't hurt to check. 2068983cfbfSMike Smith */ 2078983cfbfSMike Smith if (cio == NULL) { 2088983cfbfSMike Smith error = EINVAL; 2098983cfbfSMike Smith break; 2108983cfbfSMike Smith } 2118983cfbfSMike Smith 2128983cfbfSMike Smith /* 2138983cfbfSMike Smith * If the user specified an offset into the device list, 2148983cfbfSMike Smith * but the list has changed since they last called this 2158983cfbfSMike Smith * ioctl, tell them that the list has changed. They will 2168983cfbfSMike Smith * have to get the list from the beginning. 2178983cfbfSMike Smith */ 2188983cfbfSMike Smith if ((cio->offset != 0) 2198983cfbfSMike Smith && (cio->generation != pci_generation)){ 2208983cfbfSMike Smith cio->num_matches = 0; 2218983cfbfSMike Smith cio->status = PCI_GETCONF_LIST_CHANGED; 2228983cfbfSMike Smith error = 0; 2238983cfbfSMike Smith break; 2248983cfbfSMike Smith } 2258983cfbfSMike Smith 2268983cfbfSMike Smith /* 2278983cfbfSMike Smith * Check to see whether the user has asked for an offset 2288983cfbfSMike Smith * past the end of our list. 2298983cfbfSMike Smith */ 2308983cfbfSMike Smith if (cio->offset >= pci_numdevs) { 2318983cfbfSMike Smith cio->num_matches = 0; 2328983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 2338983cfbfSMike Smith error = 0; 2348983cfbfSMike Smith break; 2358983cfbfSMike Smith } 2368983cfbfSMike Smith 2378983cfbfSMike Smith /* get the head of the device queue */ 2388983cfbfSMike Smith devlist_head = &pci_devq; 2398983cfbfSMike Smith 2408983cfbfSMike Smith /* 2418983cfbfSMike Smith * Determine how much room we have for pci_conf structures. 2428983cfbfSMike Smith * Round the user's buffer size down to the nearest 2438983cfbfSMike Smith * multiple of sizeof(struct pci_conf) in case the user 2448983cfbfSMike Smith * didn't specify a multiple of that size. 2458983cfbfSMike Smith */ 2468983cfbfSMike Smith iolen = min(cio->match_buf_len - 2478983cfbfSMike Smith (cio->match_buf_len % sizeof(struct pci_conf)), 2488983cfbfSMike Smith pci_numdevs * sizeof(struct pci_conf)); 2498983cfbfSMike Smith 2508983cfbfSMike Smith /* 2518983cfbfSMike Smith * Since we know that iolen is a multiple of the size of 2528983cfbfSMike Smith * the pciconf union, it's okay to do this. 2538983cfbfSMike Smith */ 2548983cfbfSMike Smith ionum = iolen / sizeof(struct pci_conf); 2558983cfbfSMike Smith 2568983cfbfSMike Smith /* 2578983cfbfSMike Smith * If this test is true, the user wants the pci_conf 2588983cfbfSMike Smith * structures returned to match the supplied entries. 2598983cfbfSMike Smith */ 2608983cfbfSMike Smith if ((cio->num_patterns > 0) 2618983cfbfSMike Smith && (cio->pat_buf_len > 0)) { 2628983cfbfSMike Smith /* 2638983cfbfSMike Smith * pat_buf_len needs to be: 2648983cfbfSMike Smith * num_patterns * sizeof(struct pci_match_conf) 2658983cfbfSMike Smith * While it is certainly possible the user just 2668983cfbfSMike Smith * allocated a large buffer, but set the number of 2678983cfbfSMike Smith * matches correctly, it is far more likely that 2688983cfbfSMike Smith * their kernel doesn't match the userland utility 2698983cfbfSMike Smith * they're using. It's also possible that the user 2708983cfbfSMike Smith * forgot to initialize some variables. Yes, this 2718983cfbfSMike Smith * may be overly picky, but I hazard to guess that 2728983cfbfSMike Smith * it's far more likely to just catch folks that 2738983cfbfSMike Smith * updated their kernel but not their userland. 2748983cfbfSMike Smith */ 2758983cfbfSMike Smith if ((cio->num_patterns * 2768983cfbfSMike Smith sizeof(struct pci_match_conf)) != cio->pat_buf_len){ 2778983cfbfSMike Smith /* The user made a mistake, return an error*/ 2788983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 2798983cfbfSMike Smith printf("pci_ioctl: pat_buf_len %d != " 2808983cfbfSMike Smith "num_patterns (%d) * sizeof(struct " 2818983cfbfSMike Smith "pci_match_conf) (%d)\npci_ioctl: " 2828983cfbfSMike Smith "pat_buf_len should be = %d\n", 2838983cfbfSMike Smith cio->pat_buf_len, cio->num_patterns, 2848983cfbfSMike Smith (int)sizeof(struct pci_match_conf), 2858983cfbfSMike Smith (int)sizeof(struct pci_match_conf) * 2868983cfbfSMike Smith cio->num_patterns); 2878983cfbfSMike Smith printf("pci_ioctl: do your headers match your " 2888983cfbfSMike Smith "kernel?\n"); 2898983cfbfSMike Smith cio->num_matches = 0; 2908983cfbfSMike Smith error = EINVAL; 2918983cfbfSMike Smith break; 2928983cfbfSMike Smith } 2938983cfbfSMike Smith 2948983cfbfSMike Smith /* 2958983cfbfSMike Smith * Check the user's buffer to make sure it's readable. 2968983cfbfSMike Smith */ 2978983cfbfSMike Smith if (!useracc((caddr_t)cio->patterns, 2988983cfbfSMike Smith cio->pat_buf_len, VM_PROT_READ)) { 2998983cfbfSMike Smith printf("pci_ioctl: pattern buffer %p, " 3008983cfbfSMike Smith "length %u isn't user accessible for" 3018983cfbfSMike Smith " READ\n", cio->patterns, 3028983cfbfSMike Smith cio->pat_buf_len); 3038983cfbfSMike Smith error = EACCES; 3048983cfbfSMike Smith break; 3058983cfbfSMike Smith } 3068983cfbfSMike Smith /* 3078983cfbfSMike Smith * Allocate a buffer to hold the patterns. 3088983cfbfSMike Smith */ 3098983cfbfSMike Smith pattern_buf = malloc(cio->pat_buf_len, M_TEMP, 3108983cfbfSMike Smith M_WAITOK); 3118983cfbfSMike Smith error = copyin(cio->patterns, pattern_buf, 3128983cfbfSMike Smith cio->pat_buf_len); 3138983cfbfSMike Smith if (error != 0) 3148983cfbfSMike Smith break; 3158983cfbfSMike Smith num_patterns = cio->num_patterns; 3168983cfbfSMike Smith 3178983cfbfSMike Smith } else if ((cio->num_patterns > 0) 3188983cfbfSMike Smith || (cio->pat_buf_len > 0)) { 3198983cfbfSMike Smith /* 3208983cfbfSMike Smith * The user made a mistake, spit out an error. 3218983cfbfSMike Smith */ 3228983cfbfSMike Smith cio->status = PCI_GETCONF_ERROR; 3238983cfbfSMike Smith cio->num_matches = 0; 3248983cfbfSMike Smith printf("pci_ioctl: invalid GETCONF arguments\n"); 3258983cfbfSMike Smith error = EINVAL; 3268983cfbfSMike Smith break; 3278983cfbfSMike Smith } else 3288983cfbfSMike Smith pattern_buf = NULL; 3298983cfbfSMike Smith 3308983cfbfSMike Smith /* 3318983cfbfSMike Smith * Make sure we can write to the match buffer. 3328983cfbfSMike Smith */ 3338983cfbfSMike Smith if (!useracc((caddr_t)cio->matches, 3348983cfbfSMike Smith cio->match_buf_len, VM_PROT_WRITE)) { 3358983cfbfSMike Smith printf("pci_ioctl: match buffer %p, length %u " 3368983cfbfSMike Smith "isn't user accessible for WRITE\n", 3378983cfbfSMike Smith cio->matches, cio->match_buf_len); 3388983cfbfSMike Smith error = EACCES; 3398983cfbfSMike Smith break; 3408983cfbfSMike Smith } 3418983cfbfSMike Smith 3428983cfbfSMike Smith /* 3438983cfbfSMike Smith * Go through the list of devices and copy out the devices 3448983cfbfSMike Smith * that match the user's criteria. 3458983cfbfSMike Smith */ 3468983cfbfSMike Smith for (cio->num_matches = 0, error = 0, i = 0, 3478983cfbfSMike Smith dinfo = STAILQ_FIRST(devlist_head); 3488983cfbfSMike Smith (dinfo != NULL) && (cio->num_matches < ionum) 3498983cfbfSMike Smith && (error == 0) && (i < pci_numdevs); 3508983cfbfSMike Smith dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { 3518983cfbfSMike Smith 3528983cfbfSMike Smith if (i < cio->offset) 3538983cfbfSMike Smith continue; 3548983cfbfSMike Smith 3558983cfbfSMike Smith /* Populate pd_name and pd_unit */ 3568983cfbfSMike Smith name = NULL; 3578983cfbfSMike Smith if (dinfo->cfg.dev && dinfo->conf.pd_name[0] == '\0') 3588983cfbfSMike Smith name = device_get_name(dinfo->cfg.dev); 3598983cfbfSMike Smith if (name) { 3608983cfbfSMike Smith strncpy(dinfo->conf.pd_name, name, 3618983cfbfSMike Smith sizeof(dinfo->conf.pd_name)); 3628983cfbfSMike Smith dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; 3638983cfbfSMike Smith dinfo->conf.pd_unit = 3648983cfbfSMike Smith device_get_unit(dinfo->cfg.dev); 3658983cfbfSMike Smith } 3668983cfbfSMike Smith 3678983cfbfSMike Smith if ((pattern_buf == NULL) || 3688983cfbfSMike Smith (pci_conf_match(pattern_buf, num_patterns, 3698983cfbfSMike Smith &dinfo->conf) == 0)) { 3708983cfbfSMike Smith 3718983cfbfSMike Smith /* 3728983cfbfSMike Smith * If we've filled up the user's buffer, 3738983cfbfSMike Smith * break out at this point. Since we've 3748983cfbfSMike Smith * got a match here, we'll pick right back 3758983cfbfSMike Smith * up at the matching entry. We can also 3768983cfbfSMike Smith * tell the user that there are more matches 3778983cfbfSMike Smith * left. 3788983cfbfSMike Smith */ 3798983cfbfSMike Smith if (cio->num_matches >= ionum) 3808983cfbfSMike Smith break; 3818983cfbfSMike Smith 3828983cfbfSMike Smith error = copyout(&dinfo->conf, 3838983cfbfSMike Smith &cio->matches[cio->num_matches], 3848983cfbfSMike Smith sizeof(struct pci_conf)); 3858983cfbfSMike Smith cio->num_matches++; 3868983cfbfSMike Smith } 3878983cfbfSMike Smith } 3888983cfbfSMike Smith 3898983cfbfSMike Smith /* 3908983cfbfSMike Smith * Set the pointer into the list, so if the user is getting 3918983cfbfSMike Smith * n records at a time, where n < pci_numdevs, 3928983cfbfSMike Smith */ 3938983cfbfSMike Smith cio->offset = i; 3948983cfbfSMike Smith 3958983cfbfSMike Smith /* 3968983cfbfSMike Smith * Set the generation, the user will need this if they make 3978983cfbfSMike Smith * another ioctl call with offset != 0. 3988983cfbfSMike Smith */ 3998983cfbfSMike Smith cio->generation = pci_generation; 4008983cfbfSMike Smith 4018983cfbfSMike Smith /* 4028983cfbfSMike Smith * If this is the last device, inform the user so he won't 4038983cfbfSMike Smith * bother asking for more devices. If dinfo isn't NULL, we 4048983cfbfSMike Smith * know that there are more matches in the list because of 4058983cfbfSMike Smith * the way the traversal is done. 4068983cfbfSMike Smith */ 4078983cfbfSMike Smith if (dinfo == NULL) 4088983cfbfSMike Smith cio->status = PCI_GETCONF_LAST_DEVICE; 4098983cfbfSMike Smith else 4108983cfbfSMike Smith cio->status = PCI_GETCONF_MORE_DEVS; 4118983cfbfSMike Smith 4128983cfbfSMike Smith if (pattern_buf != NULL) 4138983cfbfSMike Smith free(pattern_buf, M_TEMP); 4148983cfbfSMike Smith 4158983cfbfSMike Smith break; 4168983cfbfSMike Smith } 4178983cfbfSMike Smith case PCIOCREAD: 4188983cfbfSMike Smith io = (struct pci_io *)data; 4198983cfbfSMike Smith switch(io->pi_width) { 4208983cfbfSMike Smith case 4: 4218983cfbfSMike Smith case 2: 4228983cfbfSMike Smith case 1: 4238983cfbfSMike Smith /* 4248983cfbfSMike Smith * Assume that the user-level bus number is 4258983cfbfSMike Smith * actually the pciN instance number. We map 4268983cfbfSMike Smith * from that to the real pcib+bus combination. 4278983cfbfSMike Smith */ 4288983cfbfSMike Smith pci = devclass_get_device(devclass_find("pci"), 4298983cfbfSMike Smith io->pi_sel.pc_bus); 4308983cfbfSMike Smith if (pci) { 4318983cfbfSMike Smith int b = pcib_get_bus(pci); 4328983cfbfSMike Smith pcib = device_get_parent(pci); 4338983cfbfSMike Smith io->pi_data = 4348983cfbfSMike Smith PCIB_READ_CONFIG(pcib, 4358983cfbfSMike Smith b, 4368983cfbfSMike Smith io->pi_sel.pc_dev, 4378983cfbfSMike Smith io->pi_sel.pc_func, 4388983cfbfSMike Smith io->pi_reg, 4398983cfbfSMike Smith io->pi_width); 4408983cfbfSMike Smith error = 0; 4418983cfbfSMike Smith } else { 4428983cfbfSMike Smith error = ENODEV; 4438983cfbfSMike Smith } 4448983cfbfSMike Smith break; 4458983cfbfSMike Smith default: 4468983cfbfSMike Smith error = ENODEV; 4478983cfbfSMike Smith break; 4488983cfbfSMike Smith } 4498983cfbfSMike Smith break; 4508983cfbfSMike Smith 4518983cfbfSMike Smith case PCIOCWRITE: 4528983cfbfSMike Smith io = (struct pci_io *)data; 4538983cfbfSMike Smith switch(io->pi_width) { 4548983cfbfSMike Smith case 4: 4558983cfbfSMike Smith case 2: 4568983cfbfSMike Smith case 1: 4578983cfbfSMike Smith /* 4588983cfbfSMike Smith * Assume that the user-level bus number is 4598983cfbfSMike Smith * actually the pciN instance number. We map 4608983cfbfSMike Smith * from that to the real pcib+bus combination. 4618983cfbfSMike Smith */ 4628983cfbfSMike Smith pci = devclass_get_device(devclass_find("pci"), 4638983cfbfSMike Smith io->pi_sel.pc_bus); 4648983cfbfSMike Smith if (pci) { 4658983cfbfSMike Smith int b = pcib_get_bus(pci); 4668983cfbfSMike Smith pcib = device_get_parent(pci); 4678983cfbfSMike Smith PCIB_WRITE_CONFIG(pcib, 4688983cfbfSMike Smith b, 4698983cfbfSMike Smith io->pi_sel.pc_dev, 4708983cfbfSMike Smith io->pi_sel.pc_func, 4718983cfbfSMike Smith io->pi_reg, 4728983cfbfSMike Smith io->pi_data, 4738983cfbfSMike Smith io->pi_width); 4748983cfbfSMike Smith error = 0; 4758983cfbfSMike Smith } else { 4768983cfbfSMike Smith error = ENODEV; 4778983cfbfSMike Smith } 4788983cfbfSMike Smith break; 4798983cfbfSMike Smith default: 4808983cfbfSMike Smith error = ENODEV; 4818983cfbfSMike Smith break; 4828983cfbfSMike Smith } 4838983cfbfSMike Smith break; 4848983cfbfSMike Smith 4858983cfbfSMike Smith default: 4868983cfbfSMike Smith error = ENOTTY; 4878983cfbfSMike Smith break; 4888983cfbfSMike Smith } 4898983cfbfSMike Smith 4908983cfbfSMike Smith return (error); 4918983cfbfSMike Smith } 4928983cfbfSMike Smith 493