static int pci_ioctl(struct dev_ioctl_args *ap) { device_t pcidev, brdev; void *confdata; const char *name; struct devlist *devlist_head; struct pci_conf_io *cio; struct pci_devinfo *dinfo; struct pci_io *io; struct pci_bar_io *bio; struct pci_match_conf *pattern_buf; struct resource_list_entry *rle; uint32_t value; size_t confsz, iolen, pbufsz; int error, ionum, i, num_patterns; #ifdef PRE7_COMPAT struct pci_conf_old conf_old; struct pci_io iodata; struct pci_io_old *io_old; struct pci_match_conf_old *pattern_buf_old; io_old = NULL; pattern_buf_old = NULL; if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR && ap->a_cmd != PCIOCGETCONF && ap->a_cmd != PCIOCGETCONF_OLD) return EPERM; #else if (!(ap->a_fflag & FWRITE) && ap->a_cmd != PCIOCGETBAR && ap->a_cmd != PCIOCGETCONF) return EPERM; #endif switch(ap->a_cmd) { #ifdef PRE7_COMPAT case PCIOCGETCONF_OLD: /* FALLTHROUGH */ #endif case PCIOCGETCONF: cio = (struct pci_conf_io *)ap->a_data; pattern_buf = NULL; num_patterns = 0; dinfo = NULL; cio->num_matches = 0; /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; break; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; break; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ #ifdef PRE7_COMPAT if (ap->a_cmd == PCIOCGETCONF_OLD) confsz = sizeof(struct pci_conf_old); else #endif confsz = sizeof(struct pci_conf); iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz), pci_numdevs * confsz); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / confsz; /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ #ifdef PRE7_COMPAT if (ap->a_cmd == PCIOCGETCONF_OLD) pbufsz = sizeof(struct pci_match_conf_old); else #endif pbufsz = sizeof(struct pci_match_conf); if (cio->num_patterns * pbufsz != cio->pat_buf_len) { /* The user made a mistake, return an error. */ cio->status = PCI_GETCONF_ERROR; error = EINVAL; break; } /* * Allocate a buffer to hold the patterns. */ #ifdef PRE7_COMPAT if (ap->a_cmd == PCIOCGETCONF_OLD) { pattern_buf_old = kmalloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf_old, cio->pat_buf_len); } else #endif { pattern_buf = kmalloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); } if (error != 0) { error = EINVAL; goto getconfexit; } num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; error = EINVAL; break; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, error = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); (dinfo != NULL) && (cio->num_matches < ionum) && (error == 0) && (i < pci_numdevs) && (dinfo != NULL); dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; /* Populate pd_name and pd_unit */ name = NULL; if (dinfo->cfg.dev) name = device_get_name(dinfo->cfg.dev); if (name) { strncpy(dinfo->conf.pd_name, name, sizeof(dinfo->conf.pd_name)); dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; dinfo->conf.pd_unit = device_get_unit(dinfo->cfg.dev); } else { dinfo->conf.pd_name[0] = '\0'; dinfo->conf.pd_unit = 0; } #ifdef PRE7_COMPAT if ((ap->a_cmd == PCIOCGETCONF_OLD && (pattern_buf_old == NULL || pci_conf_match_old(pattern_buf_old, num_patterns, &dinfo->conf) == 0)) || (ap->a_cmd == PCIOCGETCONF && (pattern_buf == NULL || pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0))) { #else if (pattern_buf == NULL || pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0) { #endif /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; #ifdef PRE7_COMPAT if (ap->a_cmd == PCIOCGETCONF_OLD) { conf_old.pc_sel.pc_bus = dinfo->conf.pc_sel.pc_bus; conf_old.pc_sel.pc_dev = dinfo->conf.pc_sel.pc_dev; conf_old.pc_sel.pc_func = dinfo->conf.pc_sel.pc_func; conf_old.pc_hdr = dinfo->conf.pc_hdr; conf_old.pc_subvendor = dinfo->conf.pc_subvendor; conf_old.pc_subdevice = dinfo->conf.pc_subdevice; conf_old.pc_vendor = dinfo->conf.pc_vendor; conf_old.pc_device = dinfo->conf.pc_device; conf_old.pc_class = dinfo->conf.pc_class; conf_old.pc_subclass = dinfo->conf.pc_subclass; conf_old.pc_progif = dinfo->conf.pc_progif; conf_old.pc_revid = dinfo->conf.pc_revid; strncpy(conf_old.pd_name, dinfo->conf.pd_name, sizeof(conf_old.pd_name)); conf_old.pd_name[PCI_MAXNAMELEN] = 0; conf_old.pd_unit = dinfo->conf.pd_unit; confdata = &conf_old; } else #endif confdata = &dinfo->conf; /* Only if we can copy it out do we count it. */ if (!(error = copyout(confdata, (caddr_t)cio->matches + confsz * cio->num_matches, confsz))) cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; getconfexit: if (pattern_buf != NULL) kfree(pattern_buf, M_TEMP); #ifdef PRE7_COMPAT if (pattern_buf_old != NULL) kfree(pattern_buf_old, M_TEMP); #endif break; #ifdef PRE7_COMPAT case PCIOCREAD_OLD: case PCIOCWRITE_OLD: io_old = (struct pci_io_old *)ap->a_data; iodata.pi_sel.pc_domain = 0; iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus; iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev; iodata.pi_sel.pc_func = io_old->pi_sel.pc_func; iodata.pi_reg = io_old->pi_reg; iodata.pi_width = io_old->pi_width; iodata.pi_data = io_old->pi_data; ap->a_data = (caddr_t)&iodata; /* FALLTHROUGH */ #endif case PCIOCREAD: case PCIOCWRITE: io = (struct pci_io *)ap->a_data; switch(io->pi_width) { case 4: case 2: case 1: /* Make sure register is in bounds and aligned. */ if (io->pi_reg < 0 || io->pi_reg + io->pi_width > PCI_REGMAX + 1 || io->pi_reg & (io->pi_width - 1)) { error = EINVAL; break; } /* * Assume that the user-level bus number is * in fact the physical PCI bus number. * Look up the grandparent, i.e. the bridge device, * so that we can issue configuration space cycles. */ pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func); if (pcidev) { brdev = device_get_parent( device_get_parent(pcidev)); #ifdef PRE7_COMPAT if (ap->a_cmd == PCIOCWRITE || ap->a_cmd == PCIOCWRITE_OLD) #else if (ap->a_cmd == PCIOCWRITE) #endif PCIB_WRITE_CONFIG(brdev, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func, io->pi_reg, io->pi_data, io->pi_width); #ifdef PRE7_COMPAT else if (ap->a_cmd == PCIOCREAD_OLD) io_old->pi_data = PCIB_READ_CONFIG(brdev, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func, io->pi_reg, io->pi_width); #endif else io->pi_data = PCIB_READ_CONFIG(brdev, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func, io->pi_reg, io->pi_width); error = 0; } else { #ifdef COMPAT_FREEBSD4 if (cmd == PCIOCREAD_OLD) { io_old->pi_data = -1; error = 0; } else #endif error = ENODEV; } break; default: error = EINVAL; break; } break; case PCIOCGETBAR: bio = (struct pci_bar_io *)ap->a_data; /* * Assume that the user-level bus number is * in fact the physical PCI bus number. */ pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, bio->pbi_sel.pc_func); if (pcidev == NULL) { error = ENODEV; break; } dinfo = device_get_ivars(pcidev); /* * Look for a resource list entry matching the requested BAR. * * XXX: This will not find BARs that are not initialized, but * maybe that is ok? */ rle = resource_list_find(&dinfo->resources, SYS_RES_MEMORY, bio->pbi_reg); if (rle == NULL) rle = resource_list_find(&dinfo->resources, SYS_RES_IOPORT, bio->pbi_reg); if (rle == NULL || rle->res == NULL) { error = EINVAL; break; } /* * Ok, we have a resource for this BAR. Read the lower * 32 bits to get any flags. */ value = pci_read_config(pcidev, bio->pbi_reg, 4); if (PCI_BAR_MEM(value)) { if (rle->type != SYS_RES_MEMORY) { error = EINVAL; break; } value &= ~PCIM_BAR_MEM_BASE; } else { if (rle->type != SYS_RES_IOPORT) { error = EINVAL; break; } value &= ~PCIM_BAR_IO_BASE; } bio->pbi_base = rman_get_start(rle->res) | value; bio->pbi_length = rman_get_size(rle->res); /* * Check the command register to determine if this BAR * is enabled. */ value = pci_read_config(pcidev, PCIR_COMMAND, 2); if (rle->type == SYS_RES_MEMORY) bio->pbi_enabled = (value & PCIM_CMD_MEMEN) != 0; else bio->pbi_enabled = (value & PCIM_CMD_PORTEN) != 0; error = 0; break; default: error = ENOTTY; break; } return (error); } #define PCI_CDEV 78 struct dev_ops pcic_ops = { { "pci", PCI_CDEV, 0 }, .d_open = pci_open, .d_close = pci_close, .d_ioctl = pci_ioctl, };
static int pci_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) { device_t pcidev; void *confdata; const char *name; struct devlist *devlist_head; struct pci_conf_io *cio = NULL; struct pci_devinfo *dinfo; struct pci_io *io; struct pci_bar_io *bio; struct pci_list_vpd_io *lvio; struct pci_match_conf *pattern_buf; struct pci_map *pm; size_t confsz, iolen, pbufsz; int error, ionum, i, num_patterns; #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 struct pci_conf_io32 *cio32 = NULL; struct pci_conf_old32 conf_old32; struct pci_match_conf_old32 *pattern_buf_old32 = NULL; #endif struct pci_conf_old conf_old; struct pci_io iodata; struct pci_io_old *io_old; struct pci_match_conf_old *pattern_buf_old = NULL; io_old = NULL; #endif if (!(flag & FWRITE)) { switch (cmd) { #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 case PCIOCGETCONF_OLD32: #endif case PCIOCGETCONF_OLD: #endif case PCIOCGETCONF: case PCIOCGETBAR: case PCIOCLISTVPD: break; default: return (EPERM); } } switch (cmd) { #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 case PCIOCGETCONF_OLD32: cio32 = (struct pci_conf_io32 *)data; cio = malloc(sizeof(struct pci_conf_io), M_TEMP, M_WAITOK); cio->pat_buf_len = cio32->pat_buf_len; cio->num_patterns = cio32->num_patterns; cio->patterns = (void *)(uintptr_t)cio32->patterns; cio->match_buf_len = cio32->match_buf_len; cio->num_matches = cio32->num_matches; cio->matches = (void *)(uintptr_t)cio32->matches; cio->offset = cio32->offset; cio->generation = cio32->generation; cio->status = cio32->status; cio32->num_matches = 0; break; #endif case PCIOCGETCONF_OLD: #endif case PCIOCGETCONF: cio = (struct pci_conf_io *)data; } switch (cmd) { #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 case PCIOCGETCONF_OLD32: #endif case PCIOCGETCONF_OLD: #endif case PCIOCGETCONF: pattern_buf = NULL; num_patterns = 0; dinfo = NULL; cio->num_matches = 0; /* * If the user specified an offset into the device list, * but the list has changed since they last called this * ioctl, tell them that the list has changed. They will * have to get the list from the beginning. */ if ((cio->offset != 0) && (cio->generation != pci_generation)){ cio->status = PCI_GETCONF_LIST_CHANGED; error = 0; goto getconfexit; } /* * Check to see whether the user has asked for an offset * past the end of our list. */ if (cio->offset >= pci_numdevs) { cio->status = PCI_GETCONF_LAST_DEVICE; error = 0; goto getconfexit; } /* get the head of the device queue */ devlist_head = &pci_devq; /* * Determine how much room we have for pci_conf structures. * Round the user's buffer size down to the nearest * multiple of sizeof(struct pci_conf) in case the user * didn't specify a multiple of that size. */ #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 if (cmd == PCIOCGETCONF_OLD32) confsz = sizeof(struct pci_conf_old32); else #endif if (cmd == PCIOCGETCONF_OLD) confsz = sizeof(struct pci_conf_old); else #endif confsz = sizeof(struct pci_conf); iolen = min(cio->match_buf_len - (cio->match_buf_len % confsz), pci_numdevs * confsz); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / confsz; /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (cio->num_patterns < pci_numdevs) && (cio->pat_buf_len > 0)) { /* * pat_buf_len needs to be: * num_patterns * sizeof(struct pci_match_conf) * While it is certainly possible the user just * allocated a large buffer, but set the number of * matches correctly, it is far more likely that * their kernel doesn't match the userland utility * they're using. It's also possible that the user * forgot to initialize some variables. Yes, this * may be overly picky, but I hazard to guess that * it's far more likely to just catch folks that * updated their kernel but not their userland. */ #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 if (cmd == PCIOCGETCONF_OLD32) pbufsz = sizeof(struct pci_match_conf_old32); else #endif if (cmd == PCIOCGETCONF_OLD) pbufsz = sizeof(struct pci_match_conf_old); else #endif pbufsz = sizeof(struct pci_match_conf); if (cio->num_patterns * pbufsz != cio->pat_buf_len) { /* The user made a mistake, return an error. */ cio->status = PCI_GETCONF_ERROR; error = EINVAL; goto getconfexit; } /* * Allocate a buffer to hold the patterns. */ #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 if (cmd == PCIOCGETCONF_OLD32) { pattern_buf_old32 = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf_old32, cio->pat_buf_len); } else #endif /* COMPAT_FREEBSD32 */ if (cmd == PCIOCGETCONF_OLD) { pattern_buf_old = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf_old, cio->pat_buf_len); } else #endif /* PRE7_COMPAT */ { pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); } if (error != 0) { error = EINVAL; goto getconfexit; } num_patterns = cio->num_patterns; } else if ((cio->num_patterns > 0) || (cio->pat_buf_len > 0)) { /* * The user made a mistake, spit out an error. */ cio->status = PCI_GETCONF_ERROR; error = EINVAL; goto getconfexit; } /* * Go through the list of devices and copy out the devices * that match the user's criteria. */ for (cio->num_matches = 0, i = 0, dinfo = STAILQ_FIRST(devlist_head); dinfo != NULL; dinfo = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; /* Populate pd_name and pd_unit */ name = NULL; if (dinfo->cfg.dev) name = device_get_name(dinfo->cfg.dev); if (name) { strncpy(dinfo->conf.pd_name, name, sizeof(dinfo->conf.pd_name)); dinfo->conf.pd_name[PCI_MAXNAMELEN] = 0; dinfo->conf.pd_unit = device_get_unit(dinfo->cfg.dev); } else { dinfo->conf.pd_name[0] = '\0'; dinfo->conf.pd_unit = 0; } #ifdef PRE7_COMPAT if ( #ifdef COMPAT_FREEBSD32 (cmd == PCIOCGETCONF_OLD32 && (pattern_buf_old32 == NULL || pci_conf_match_old32(pattern_buf_old32, num_patterns, &dinfo->conf) == 0)) || #endif (cmd == PCIOCGETCONF_OLD && (pattern_buf_old == NULL || pci_conf_match_old(pattern_buf_old, num_patterns, &dinfo->conf) == 0)) || (cmd == PCIOCGETCONF && (pattern_buf == NULL || pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0))) { #else if (pattern_buf == NULL || pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0) { #endif /* * If we've filled up the user's buffer, * break out at this point. Since we've * got a match here, we'll pick right back * up at the matching entry. We can also * tell the user that there are more matches * left. */ if (cio->num_matches >= ionum) break; #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 if (cmd == PCIOCGETCONF_OLD32) { conf_old32.pc_sel.pc_bus = dinfo->conf.pc_sel.pc_bus; conf_old32.pc_sel.pc_dev = dinfo->conf.pc_sel.pc_dev; conf_old32.pc_sel.pc_func = dinfo->conf.pc_sel.pc_func; conf_old32.pc_hdr = dinfo->conf.pc_hdr; conf_old32.pc_subvendor = dinfo->conf.pc_subvendor; conf_old32.pc_subdevice = dinfo->conf.pc_subdevice; conf_old32.pc_vendor = dinfo->conf.pc_vendor; conf_old32.pc_device = dinfo->conf.pc_device; conf_old32.pc_class = dinfo->conf.pc_class; conf_old32.pc_subclass = dinfo->conf.pc_subclass; conf_old32.pc_progif = dinfo->conf.pc_progif; conf_old32.pc_revid = dinfo->conf.pc_revid; strncpy(conf_old32.pd_name, dinfo->conf.pd_name, sizeof(conf_old32.pd_name)); conf_old32.pd_name[PCI_MAXNAMELEN] = 0; conf_old32.pd_unit = (uint32_t)dinfo->conf.pd_unit; confdata = &conf_old32; } else #endif /* COMPAT_FREEBSD32 */ if (cmd == PCIOCGETCONF_OLD) { conf_old.pc_sel.pc_bus = dinfo->conf.pc_sel.pc_bus; conf_old.pc_sel.pc_dev = dinfo->conf.pc_sel.pc_dev; conf_old.pc_sel.pc_func = dinfo->conf.pc_sel.pc_func; conf_old.pc_hdr = dinfo->conf.pc_hdr; conf_old.pc_subvendor = dinfo->conf.pc_subvendor; conf_old.pc_subdevice = dinfo->conf.pc_subdevice; conf_old.pc_vendor = dinfo->conf.pc_vendor; conf_old.pc_device = dinfo->conf.pc_device; conf_old.pc_class = dinfo->conf.pc_class; conf_old.pc_subclass = dinfo->conf.pc_subclass; conf_old.pc_progif = dinfo->conf.pc_progif; conf_old.pc_revid = dinfo->conf.pc_revid; strncpy(conf_old.pd_name, dinfo->conf.pd_name, sizeof(conf_old.pd_name)); conf_old.pd_name[PCI_MAXNAMELEN] = 0; conf_old.pd_unit = dinfo->conf.pd_unit; confdata = &conf_old; } else #endif /* PRE7_COMPAT */ confdata = &dinfo->conf; error = copyout(confdata, (caddr_t)cio->matches + confsz * cio->num_matches, confsz); if (error) break; cio->num_matches++; } } /* * Set the pointer into the list, so if the user is getting * n records at a time, where n < pci_numdevs, */ cio->offset = i; /* * Set the generation, the user will need this if they make * another ioctl call with offset != 0. */ cio->generation = pci_generation; /* * If this is the last device, inform the user so he won't * bother asking for more devices. If dinfo isn't NULL, we * know that there are more matches in the list because of * the way the traversal is done. */ if (dinfo == NULL) cio->status = PCI_GETCONF_LAST_DEVICE; else cio->status = PCI_GETCONF_MORE_DEVS; getconfexit: #ifdef PRE7_COMPAT #ifdef COMPAT_FREEBSD32 if (cmd == PCIOCGETCONF_OLD32) { cio32->status = cio->status; cio32->generation = cio->generation; cio32->offset = cio->offset; cio32->num_matches = cio->num_matches; free(cio, M_TEMP); } if (pattern_buf_old32 != NULL) free(pattern_buf_old32, M_TEMP); #endif if (pattern_buf_old != NULL) free(pattern_buf_old, M_TEMP); #endif if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; #ifdef PRE7_COMPAT case PCIOCREAD_OLD: case PCIOCWRITE_OLD: io_old = (struct pci_io_old *)data; iodata.pi_sel.pc_domain = 0; iodata.pi_sel.pc_bus = io_old->pi_sel.pc_bus; iodata.pi_sel.pc_dev = io_old->pi_sel.pc_dev; iodata.pi_sel.pc_func = io_old->pi_sel.pc_func; iodata.pi_reg = io_old->pi_reg; iodata.pi_width = io_old->pi_width; iodata.pi_data = io_old->pi_data; data = (caddr_t)&iodata; /* FALLTHROUGH */ #endif case PCIOCREAD: case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { case 4: case 2: case 1: /* Make sure register is not negative and aligned. */ if (io->pi_reg < 0 || io->pi_reg & (io->pi_width - 1)) { error = EINVAL; break; } /* * Assume that the user-level bus number is * in fact the physical PCI bus number. * Look up the grandparent, i.e. the bridge device, * so that we can issue configuration space cycles. */ pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func); if (pcidev) { #ifdef PRE7_COMPAT if (cmd == PCIOCWRITE || cmd == PCIOCWRITE_OLD) #else if (cmd == PCIOCWRITE) #endif pci_write_config(pcidev, io->pi_reg, io->pi_data, io->pi_width); #ifdef PRE7_COMPAT else if (cmd == PCIOCREAD_OLD) io_old->pi_data = pci_read_config(pcidev, io->pi_reg, io->pi_width); #endif else io->pi_data = pci_read_config(pcidev, io->pi_reg, io->pi_width); error = 0; } else { #ifdef COMPAT_FREEBSD4 if (cmd == PCIOCREAD_OLD) { io_old->pi_data = -1; error = 0; } else #endif error = ENODEV; } break; default: error = EINVAL; break; } break; case PCIOCGETBAR: bio = (struct pci_bar_io *)data; /* * Assume that the user-level bus number is * in fact the physical PCI bus number. */ pcidev = pci_find_dbsf(bio->pbi_sel.pc_domain, bio->pbi_sel.pc_bus, bio->pbi_sel.pc_dev, bio->pbi_sel.pc_func); if (pcidev == NULL) { error = ENODEV; break; } pm = pci_find_bar(pcidev, bio->pbi_reg); if (pm == NULL) { error = EINVAL; break; } bio->pbi_base = pm->pm_value; bio->pbi_length = (pci_addr_t)1 << pm->pm_size; bio->pbi_enabled = pci_bar_enabled(pcidev, pm); error = 0; break; case PCIOCATTACHED: error = 0; io = (struct pci_io *)data; pcidev = pci_find_dbsf(io->pi_sel.pc_domain, io->pi_sel.pc_bus, io->pi_sel.pc_dev, io->pi_sel.pc_func); if (pcidev != NULL) io->pi_data = device_is_attached(pcidev); else error = ENODEV; break; case PCIOCLISTVPD: lvio = (struct pci_list_vpd_io *)data; /* * Assume that the user-level bus number is * in fact the physical PCI bus number. */ pcidev = pci_find_dbsf(lvio->plvi_sel.pc_domain, lvio->plvi_sel.pc_bus, lvio->plvi_sel.pc_dev, lvio->plvi_sel.pc_func); if (pcidev == NULL) { error = ENODEV; break; } error = pci_list_vpd(pcidev, lvio); break; default: error = ENOTTY; break; } return (error); }