📄 pci.c
字号:
for (i = 0; i < cfg->nummaps; i++) { pcimap *m = &cfg->map[i]; printf("\tmap[%d]: type %x, range %2d, base %08x, size %2d\n", i, m->type, m->ln2range, m->base, m->ln2size); } } pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */}/* scan one PCI bus for devices */static intpci_probebus(int bus){ pcicfgregs probe; int bushigh = bus;#ifdef SIMOS#undef PCI_SLOTMAX#define PCI_SLOTMAX 0#endif bzero(&probe, sizeof probe); /* XXX KDM */ /* probe.parent = pci_bridgeto(bus); */ probe.bus = bus; for (probe.slot = 0; probe.slot <= PCI_SLOTMAX; probe.slot++) { int pcifunchigh = 0; for (probe.func = 0; probe.func <= pcifunchigh; probe.func++) { struct pci_devinfo *dinfo = pci_readcfg(&probe); if (dinfo != NULL) { if (dinfo->cfg.mfdev) pcifunchigh = 7; /* * XXX: Temporarily move pci_addcfg() up before * the use of cfg->subordinatebus. This is * necessary, since pci_addcfg() calls the * device's probe(), which may read the bus# * from some device dependent register of * some host to PCI bridges. The probe will * eventually be moved to pci_readcfg(), and * pci_addcfg() will then be moved back down * below the conditional statement ... */ pci_addcfg(dinfo); if (bushigh < dinfo->cfg.subordinatebus) bushigh = dinfo->cfg.subordinatebus; if (bushigh < dinfo->cfg.secondarybus) bushigh = dinfo->cfg.secondarybus; /* XXX KDM */ /* cfg = NULL; we don't own this anymore ... */ } } } return (bushigh);}/* scan a PCI bus tree reached through one PCI attachment point */intpci_probe(pciattach *parent){ int bushigh; int bus = 0; STAILQ_INIT(&pci_devq); bushigh = pci_bushigh(); while (bus <= bushigh) { int newbushigh; printf("Probing for devices on PCI bus %d:\n", bus); newbushigh = pci_probebus(bus); if (bushigh < newbushigh) bushigh = newbushigh; bus++; } return (bushigh);}/* * This is the user interface to PCI configuration space. */ static intpci_open(dev_t dev, int oflags, int devtype, struct proc *p){ if ((oflags & FWRITE) && securelevel > 0) { return EPERM; } return 0;}static intpci_close(dev_t dev, int flag, int devtype, struct proc *p){ return 0;}/* * Match a single pci_conf structure against an array of pci_match_conf * structures. The first argument, 'matches', is an array of num_matches * pci_match_conf structures. match_buf is a pointer to the pci_conf * structure that will be compared to every entry in the matches array. * This function returns 1 on failure, 0 on success. */static intpci_conf_match(struct pci_match_conf *matches, int num_matches, struct pci_conf *match_buf){ int i; if ((matches == NULL) || (match_buf == NULL) || (num_matches <= 0)) return(1); for (i = 0; i < num_matches; i++) { /* * I'm not sure why someone would do this...but... */ if (matches[i].flags == PCI_GETCONF_NO_MATCH) continue; /* * Look at each of the match flags. If it's set, do the * comparison. If the comparison fails, we don't have a * match, go on to the next item if there is one. */ if (((matches[i].flags & PCI_GETCONF_MATCH_BUS) != 0) && (match_buf->pc_sel.pc_bus != matches[i].pc_sel.pc_bus)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEV) != 0) && (match_buf->pc_sel.pc_dev != matches[i].pc_sel.pc_dev)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_FUNC) != 0) && (match_buf->pc_sel.pc_func != matches[i].pc_sel.pc_func)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_VENDOR) != 0) && (match_buf->pc_vendor != matches[i].pc_vendor)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_DEVICE) != 0) && (match_buf->pc_device != matches[i].pc_device)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_CLASS) != 0) && (match_buf->pc_class != matches[i].pc_class)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_UNIT) != 0) && (match_buf->pd_unit != matches[i].pd_unit)) continue; if (((matches[i].flags & PCI_GETCONF_MATCH_NAME) != 0) && (strncmp(matches[i].pd_name, match_buf->pd_name, sizeof(match_buf->pd_name)) != 0)) continue; return(0); } return(1);}static intpci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p){ struct pci_io *io; int error; if (!(flag & FWRITE)) return EPERM; switch(cmd) { case PCIOCGETCONF: { struct pci_devinfo *dinfo; struct pci_conf_io *cio; struct devlist *devlist_head; struct pci_match_conf *pattern_buf; int num_patterns; size_t iolen; int ionum, i; cio = (struct pci_conf_io *)data; num_patterns = 0; dinfo = NULL; /* * Hopefully the user won't pass in a null pointer, but it * can't hurt to check. */ if (cio == NULL) { error = EINVAL; break; } /* * 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->num_matches = 0; 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->num_matches = 0; 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. */ iolen = min(cio->match_buf_len - (cio->match_buf_len % sizeof(struct pci_conf)), pci_numdevs * sizeof(struct pci_conf)); /* * Since we know that iolen is a multiple of the size of * the pciconf union, it's okay to do this. */ ionum = iolen / sizeof(struct pci_conf); /* * If this test is true, the user wants the pci_conf * structures returned to match the supplied entries. */ if ((cio->num_patterns > 0) && (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. */ if ((cio->num_patterns * sizeof(struct pci_match_conf)) != cio->pat_buf_len){ /* The user made a mistake, return an error*/ cio->status = PCI_GETCONF_ERROR; printf("pci_ioctl: pat_buf_len %d != " "num_patterns (%d) * sizeof(struct " "pci_match_conf) (%d)\npci_ioctl: " "pat_buf_len should be = %d\n", cio->pat_buf_len, cio->num_patterns, sizeof(struct pci_match_conf), sizeof(struct pci_match_conf) * cio->num_patterns); printf("pci_ioctl: do your headers match your " "kernel?\n"); cio->num_matches = 0; error = EINVAL; break; } /* * Check the user's buffer to make sure it's readable. */ if ((error = useracc((caddr_t)cio->patterns, cio->pat_buf_len, B_READ)) != 1){ printf("pci_ioctl: pattern buffer %p, " "length %u isn't user accessible for" " READ\n", cio->patterns, cio->pat_buf_len); error = EACCES; break; } /* * Allocate a buffer to hold the patterns. */ pattern_buf = malloc(cio->pat_buf_len, M_TEMP, M_WAITOK); error = copyin(cio->patterns, pattern_buf, cio->pat_buf_len); if (error != 0) break; 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; cio->num_matches = 0; printf("pci_ioctl: invalid GETCONF arguments\n"); error = EINVAL; break; } else pattern_buf = NULL; /* * Make sure we can write to the match buffer. */ if ((error = useracc((caddr_t)cio->matches, cio->match_buf_len, B_WRITE)) != 1) { printf("pci_ioctl: match buffer %p, length %u " "isn't user accessible for WRITE\n", cio->matches, cio->match_buf_len); error = EACCES; 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 = STAILQ_NEXT(dinfo, pci_links), i++) { if (i < cio->offset) continue; if ((pattern_buf == NULL) || (pci_conf_match(pattern_buf, num_patterns, &dinfo->conf) == 0)) { /* * 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; error = copyout(&dinfo->conf, &cio->matches[cio->num_matches], sizeof(struct pci_conf)); 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; if (pattern_buf != NULL) free(pattern_buf, M_TEMP); break; } case PCIOCREAD: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; io->pi_data = pci_cfgread(&probe, io->pi_reg, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; case PCIOCWRITE: io = (struct pci_io *)data; switch(io->pi_width) { pcicfgregs probe; case 4: case 2: case 1: probe.bus = io->pi_sel.pc_bus; probe.slot = io->pi_sel.pc_dev; probe.func = io->pi_sel.pc_func; pci_cfgwrite(&probe, io->pi_reg, io->pi_data, io->pi_width); error = 0; break; default: error = ENODEV; break; } break; default: error = ENOTTY; break; } return (error);}#define PCI_CDEV 78static struct cdevsw pcicdev = { pci_open, pci_close, noread, nowrite, pci_ioctl, nostop, noreset, nodevtotty, seltrue, nommap, nostrategy, "pci", 0, PCI_CDEV};#ifdef DEVFSstatic void *pci_devfs_token;#endifstatic voidpci_cdevinit(void *dummy){ dev_t dev; dev = makedev(PCI_CDEV, 0); cdevsw_add(&dev, &pcicdev, NULL);#ifdef DEVFS pci_devfs_token = devfs_add_devswf(&pcicdev, 0, DV_CHR, UID_ROOT, GID_WHEEL, 0644, "pci");#endif}SYSINIT(pcidev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+PCI_CDEV, pci_cdevinit, NULL);#endif /* NPCI > 0 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -