mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-10 14:02:43 +00:00
Revive PCIConf.
Submitted by: "Kenneth D. Merry" <ken@plutotech.com>
This commit is contained in:
parent
8d2fbde504
commit
06915ea69a
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=39231
@ -23,7 +23,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: pci.c,v 1.85 1998/08/13 19:12:20 gibbs Exp $
|
||||
* $Id: pci.c,v 1.86 1998/09/06 22:41:41 tegge Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -39,12 +39,16 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/buf.h>
|
||||
#ifdef DEVFS
|
||||
#include <sys/devfsext.h>
|
||||
#endif /* DEVFS */
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <vm/vm_extern.h>
|
||||
|
||||
#include <pci/pcireg.h>
|
||||
#include <pci/pcivar.h>
|
||||
@ -54,6 +58,10 @@
|
||||
#include <machine/smp.h>
|
||||
#endif /* APIC_IO */
|
||||
|
||||
STAILQ_HEAD(devlist, pci_devinfo) pci_devq;
|
||||
u_int32_t pci_numdevs = 0;
|
||||
u_int32_t pci_generation = 0;
|
||||
|
||||
/* return highest PCI bus number known to be used, or -1 if none */
|
||||
|
||||
static int
|
||||
@ -305,23 +313,30 @@ pci_hdrtypedata(pcicfgregs *cfg)
|
||||
|
||||
/* read configuration header into pcicfgrect structure */
|
||||
|
||||
static pcicfgregs *
|
||||
static struct pci_devinfo *
|
||||
pci_readcfg(pcicfgregs *probe)
|
||||
{
|
||||
pcicfgregs *cfg = NULL;
|
||||
struct pci_devinfo *devlist_entry;
|
||||
struct devlist *devlist_head;
|
||||
|
||||
devlist_head = &pci_devq;
|
||||
|
||||
devlist_entry = NULL;
|
||||
|
||||
if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) {
|
||||
cfg = malloc(sizeof (pcicfgregs), M_DEVBUF, M_WAITOK);
|
||||
if (cfg == NULL)
|
||||
return (cfg);
|
||||
devlist_entry = malloc(sizeof(struct pci_devinfo),
|
||||
M_DEVBUF, M_WAITOK);
|
||||
if (devlist_entry == NULL)
|
||||
return (NULL);
|
||||
|
||||
cfg = &devlist_entry->cfg;
|
||||
|
||||
bzero(cfg, sizeof *cfg);
|
||||
|
||||
cfg->bus = probe->bus;
|
||||
cfg->slot = probe->slot;
|
||||
cfg->func = probe->func;
|
||||
cfg->parent = probe->parent;
|
||||
|
||||
cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2);
|
||||
cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2);
|
||||
cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2);
|
||||
@ -375,30 +390,64 @@ pci_readcfg(pcicfgregs *probe)
|
||||
|
||||
pci_fixancient(cfg);
|
||||
pci_hdrtypedata(cfg);
|
||||
|
||||
STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links);
|
||||
|
||||
devlist_entry->conf.pc_sel.pc_bus = cfg->bus;
|
||||
devlist_entry->conf.pc_sel.pc_dev = cfg->slot;
|
||||
devlist_entry->conf.pc_sel.pc_func = cfg->func;
|
||||
devlist_entry->conf.pc_hdr = cfg->hdrtype;
|
||||
|
||||
devlist_entry->conf.pc_subvendor = cfg->subvendor;
|
||||
devlist_entry->conf.pc_subdevice = cfg->subdevice;
|
||||
devlist_entry->conf.pc_vendor = cfg->vendor;
|
||||
devlist_entry->conf.pc_device = cfg->device;
|
||||
|
||||
devlist_entry->conf.pc_class = cfg->baseclass;
|
||||
devlist_entry->conf.pc_subclass = cfg->subclass;
|
||||
devlist_entry->conf.pc_progif = cfg->progif;
|
||||
devlist_entry->conf.pc_revid = cfg->revid;
|
||||
|
||||
pci_numdevs++;
|
||||
pci_generation++;
|
||||
}
|
||||
return (cfg);
|
||||
return (devlist_entry);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* free pcicfgregs structure and all depending data structures */
|
||||
|
||||
static int
|
||||
pci_freecfg(pcicfgregs *cfg)
|
||||
pci_freecfg(struct pci_devinfo *dinfo)
|
||||
{
|
||||
if (cfg->hdrspec != NULL)
|
||||
free(cfg->hdrspec, M_DEVBUF);
|
||||
if (cfg->map != NULL)
|
||||
free(cfg->map, M_DEVBUF);
|
||||
free(cfg, M_DEVBUF);
|
||||
struct devlist *devlist_head;
|
||||
|
||||
devlist_head = &pci_devq;
|
||||
|
||||
if (dinfo->cfg.hdrspec != NULL)
|
||||
free(dinfo->cfg.hdrspec, M_DEVBUF);
|
||||
if (dinfo->cfg.map != NULL)
|
||||
free(dinfo->cfg.map, M_DEVBUF);
|
||||
/* XXX this hasn't been tested */
|
||||
STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
|
||||
free(dinfo, M_DEVBUF);
|
||||
|
||||
/* increment the generation count */
|
||||
pci_generation++;
|
||||
|
||||
/* we're losing one device */
|
||||
pci_numdevs--;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
pci_addcfg(pcicfgregs *cfg)
|
||||
pci_addcfg(struct pci_devinfo *dinfo)
|
||||
{
|
||||
if (bootverbose) {
|
||||
int i;
|
||||
pcicfgregs *cfg = &dinfo->cfg;
|
||||
|
||||
printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n",
|
||||
cfg->vendor, cfg->device, cfg->revid);
|
||||
printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n",
|
||||
@ -420,7 +469,7 @@ pci_addcfg(pcicfgregs *cfg)
|
||||
i, m->type, m->ln2range, m->base, m->ln2size);
|
||||
}
|
||||
}
|
||||
pci_drvattach(cfg); /* XXX currently defined in pci_compat.c */
|
||||
pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */
|
||||
}
|
||||
|
||||
/* return pointer to device that is a bridge to this bus */
|
||||
@ -445,14 +494,15 @@ pci_probebus(int bus)
|
||||
#endif
|
||||
|
||||
bzero(&probe, sizeof probe);
|
||||
probe.parent = pci_bridgeto(bus);
|
||||
/* 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++) {
|
||||
pcicfgregs *cfg = pci_readcfg(&probe);
|
||||
if (cfg != NULL) {
|
||||
if (cfg->mfdev)
|
||||
struct pci_devinfo *dinfo = pci_readcfg(&probe);
|
||||
if (dinfo != NULL) {
|
||||
if (dinfo->cfg.mfdev)
|
||||
pcifunchigh = 7;
|
||||
/*
|
||||
* XXX: Temporarily move pci_addcfg() up before
|
||||
@ -465,12 +515,13 @@ pci_probebus(int bus)
|
||||
* pci_addcfg() will then be moved back down
|
||||
* below the conditional statement ...
|
||||
*/
|
||||
pci_addcfg(cfg);
|
||||
pci_addcfg(dinfo);
|
||||
|
||||
if (bushigh < cfg->subordinatebus)
|
||||
bushigh = cfg->subordinatebus;
|
||||
if (bushigh < dinfo->cfg.subordinatebus)
|
||||
bushigh = dinfo->cfg.subordinatebus;
|
||||
|
||||
cfg = NULL; /* we don't own this anymore ... */
|
||||
/* XXX KDM */
|
||||
/* cfg = NULL; we don't own this anymore ... */
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -485,6 +536,8 @@ pci_probe(pciattach *parent)
|
||||
int bushigh;
|
||||
int bus = 0;
|
||||
|
||||
STAILQ_INIT(&pci_devq);
|
||||
|
||||
bushigh = pci_bushigh();
|
||||
while (bus <= bushigh) {
|
||||
int newbushigh;
|
||||
@ -518,33 +571,301 @@ pci_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 int
|
||||
pci_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 int
|
||||
pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
|
||||
{
|
||||
struct pci_io *io;
|
||||
int error;
|
||||
|
||||
if (cmd != PCIOCGETCONF && !(flag & FWRITE))
|
||||
if (!(flag & FWRITE))
|
||||
return EPERM;
|
||||
|
||||
|
||||
switch(cmd) {
|
||||
case PCIOCGETCONF:
|
||||
#ifdef NOTYET
|
||||
static struct pci_conf *pci_dev_list;
|
||||
static unsigned pci_dev_list_count;
|
||||
static unsigned pci_dev_list_size;
|
||||
{
|
||||
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;
|
||||
iolen = min(cio->pci_len,
|
||||
pci_dev_list_count * sizeof(struct pci_conf));
|
||||
cio->pci_len = pci_dev_list_count * sizeof(struct pci_conf);
|
||||
|
||||
error = copyout(pci_dev_list, cio->pci_buf, iolen);
|
||||
#else
|
||||
error = ENODEV;
|
||||
#endif
|
||||
break;
|
||||
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 %#lx, "
|
||||
"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 %#lx, 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) {
|
||||
|
@ -1,6 +1,3 @@
|
||||
#ifndef PCI_COMPAT
|
||||
#define PCI_COMPAT
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 1997, Stefan Esser <se@freebsd.org>
|
||||
* All rights reserved.
|
||||
@ -26,10 +23,20 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: pcivar.h,v 1.19 1998/07/22 08:39:08 dfr Exp $
|
||||
* $Id: pcivar.h,v 1.20 1998/08/13 19:12:20 gibbs Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PCIVAR_H_
|
||||
#define _PCIVAR_H_
|
||||
|
||||
#ifndef PCI_COMPAT
|
||||
#define PCI_COMPAT
|
||||
#endif
|
||||
|
||||
#include <pci/pci_ioctl.h> /* XXX KDM */
|
||||
#include <sys/queue.h>
|
||||
|
||||
/* some PCI bus constants */
|
||||
|
||||
#define PCI_BUSMAX 255 /* highest supported bus number */
|
||||
@ -65,8 +72,6 @@ typedef struct {
|
||||
/* config header information common to all header types */
|
||||
|
||||
typedef struct pcicfg {
|
||||
struct pcicfg *parent;
|
||||
struct pcicfg *next;
|
||||
pcimap *map; /* pointer to array of PCI maps */
|
||||
void *hdrspec; /* pointer to header type specific data */
|
||||
|
||||
@ -153,10 +158,20 @@ typedef struct pciattach {
|
||||
struct pciattach *next;
|
||||
} pciattach;
|
||||
|
||||
struct pci_devinfo {
|
||||
STAILQ_ENTRY(pci_devinfo) pci_links;
|
||||
struct pci_device *device; /* should this be ifdefed? */
|
||||
pcicfgregs cfg;
|
||||
struct pci_conf conf;
|
||||
};
|
||||
|
||||
extern u_int32_t pci_numdevs;
|
||||
|
||||
|
||||
/* externally visible functions */
|
||||
|
||||
int pci_probe (pciattach *attach);
|
||||
void pci_drvattach(pcicfgregs *cfg);
|
||||
void pci_drvattach(struct pci_devinfo *dinfo);
|
||||
|
||||
/* low level PCI config register functions provided by pcibus.c */
|
||||
|
||||
@ -208,3 +223,4 @@ int pci_unmap_int (pcici_t tag);
|
||||
int pci_register_lkm (struct pci_device *dvp, int if_revision);
|
||||
|
||||
#endif /* PCI_COMPAT */
|
||||
#endif /* _PCIVAR_H_ */
|
||||
|
395
sys/pci/pci.c
395
sys/pci/pci.c
@ -23,7 +23,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: pci.c,v 1.85 1998/08/13 19:12:20 gibbs Exp $
|
||||
* $Id: pci.c,v 1.86 1998/09/06 22:41:41 tegge Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -39,12 +39,16 @@
|
||||
#include <sys/fcntl.h>
|
||||
#include <sys/conf.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/buf.h>
|
||||
#ifdef DEVFS
|
||||
#include <sys/devfsext.h>
|
||||
#endif /* DEVFS */
|
||||
|
||||
#include <vm/vm.h>
|
||||
#include <vm/pmap.h>
|
||||
#include <vm/vm_extern.h>
|
||||
|
||||
#include <pci/pcireg.h>
|
||||
#include <pci/pcivar.h>
|
||||
@ -54,6 +58,10 @@
|
||||
#include <machine/smp.h>
|
||||
#endif /* APIC_IO */
|
||||
|
||||
STAILQ_HEAD(devlist, pci_devinfo) pci_devq;
|
||||
u_int32_t pci_numdevs = 0;
|
||||
u_int32_t pci_generation = 0;
|
||||
|
||||
/* return highest PCI bus number known to be used, or -1 if none */
|
||||
|
||||
static int
|
||||
@ -305,23 +313,30 @@ pci_hdrtypedata(pcicfgregs *cfg)
|
||||
|
||||
/* read configuration header into pcicfgrect structure */
|
||||
|
||||
static pcicfgregs *
|
||||
static struct pci_devinfo *
|
||||
pci_readcfg(pcicfgregs *probe)
|
||||
{
|
||||
pcicfgregs *cfg = NULL;
|
||||
struct pci_devinfo *devlist_entry;
|
||||
struct devlist *devlist_head;
|
||||
|
||||
devlist_head = &pci_devq;
|
||||
|
||||
devlist_entry = NULL;
|
||||
|
||||
if (pci_cfgread(probe, PCIR_DEVVENDOR, 4) != -1) {
|
||||
cfg = malloc(sizeof (pcicfgregs), M_DEVBUF, M_WAITOK);
|
||||
if (cfg == NULL)
|
||||
return (cfg);
|
||||
devlist_entry = malloc(sizeof(struct pci_devinfo),
|
||||
M_DEVBUF, M_WAITOK);
|
||||
if (devlist_entry == NULL)
|
||||
return (NULL);
|
||||
|
||||
cfg = &devlist_entry->cfg;
|
||||
|
||||
bzero(cfg, sizeof *cfg);
|
||||
|
||||
cfg->bus = probe->bus;
|
||||
cfg->slot = probe->slot;
|
||||
cfg->func = probe->func;
|
||||
cfg->parent = probe->parent;
|
||||
|
||||
cfg->vendor = pci_cfgread(cfg, PCIR_VENDOR, 2);
|
||||
cfg->device = pci_cfgread(cfg, PCIR_DEVICE, 2);
|
||||
cfg->cmdreg = pci_cfgread(cfg, PCIR_COMMAND, 2);
|
||||
@ -375,30 +390,64 @@ pci_readcfg(pcicfgregs *probe)
|
||||
|
||||
pci_fixancient(cfg);
|
||||
pci_hdrtypedata(cfg);
|
||||
|
||||
STAILQ_INSERT_TAIL(devlist_head, devlist_entry, pci_links);
|
||||
|
||||
devlist_entry->conf.pc_sel.pc_bus = cfg->bus;
|
||||
devlist_entry->conf.pc_sel.pc_dev = cfg->slot;
|
||||
devlist_entry->conf.pc_sel.pc_func = cfg->func;
|
||||
devlist_entry->conf.pc_hdr = cfg->hdrtype;
|
||||
|
||||
devlist_entry->conf.pc_subvendor = cfg->subvendor;
|
||||
devlist_entry->conf.pc_subdevice = cfg->subdevice;
|
||||
devlist_entry->conf.pc_vendor = cfg->vendor;
|
||||
devlist_entry->conf.pc_device = cfg->device;
|
||||
|
||||
devlist_entry->conf.pc_class = cfg->baseclass;
|
||||
devlist_entry->conf.pc_subclass = cfg->subclass;
|
||||
devlist_entry->conf.pc_progif = cfg->progif;
|
||||
devlist_entry->conf.pc_revid = cfg->revid;
|
||||
|
||||
pci_numdevs++;
|
||||
pci_generation++;
|
||||
}
|
||||
return (cfg);
|
||||
return (devlist_entry);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* free pcicfgregs structure and all depending data structures */
|
||||
|
||||
static int
|
||||
pci_freecfg(pcicfgregs *cfg)
|
||||
pci_freecfg(struct pci_devinfo *dinfo)
|
||||
{
|
||||
if (cfg->hdrspec != NULL)
|
||||
free(cfg->hdrspec, M_DEVBUF);
|
||||
if (cfg->map != NULL)
|
||||
free(cfg->map, M_DEVBUF);
|
||||
free(cfg, M_DEVBUF);
|
||||
struct devlist *devlist_head;
|
||||
|
||||
devlist_head = &pci_devq;
|
||||
|
||||
if (dinfo->cfg.hdrspec != NULL)
|
||||
free(dinfo->cfg.hdrspec, M_DEVBUF);
|
||||
if (dinfo->cfg.map != NULL)
|
||||
free(dinfo->cfg.map, M_DEVBUF);
|
||||
/* XXX this hasn't been tested */
|
||||
STAILQ_REMOVE(devlist_head, dinfo, pci_devinfo, pci_links);
|
||||
free(dinfo, M_DEVBUF);
|
||||
|
||||
/* increment the generation count */
|
||||
pci_generation++;
|
||||
|
||||
/* we're losing one device */
|
||||
pci_numdevs--;
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
pci_addcfg(pcicfgregs *cfg)
|
||||
pci_addcfg(struct pci_devinfo *dinfo)
|
||||
{
|
||||
if (bootverbose) {
|
||||
int i;
|
||||
pcicfgregs *cfg = &dinfo->cfg;
|
||||
|
||||
printf("found->\tvendor=0x%04x, dev=0x%04x, revid=0x%02x\n",
|
||||
cfg->vendor, cfg->device, cfg->revid);
|
||||
printf("\tclass=%02x-%02x-%02x, hdrtype=0x%02x, mfdev=%d\n",
|
||||
@ -420,7 +469,7 @@ pci_addcfg(pcicfgregs *cfg)
|
||||
i, m->type, m->ln2range, m->base, m->ln2size);
|
||||
}
|
||||
}
|
||||
pci_drvattach(cfg); /* XXX currently defined in pci_compat.c */
|
||||
pci_drvattach(dinfo); /* XXX currently defined in pci_compat.c */
|
||||
}
|
||||
|
||||
/* return pointer to device that is a bridge to this bus */
|
||||
@ -445,14 +494,15 @@ pci_probebus(int bus)
|
||||
#endif
|
||||
|
||||
bzero(&probe, sizeof probe);
|
||||
probe.parent = pci_bridgeto(bus);
|
||||
/* 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++) {
|
||||
pcicfgregs *cfg = pci_readcfg(&probe);
|
||||
if (cfg != NULL) {
|
||||
if (cfg->mfdev)
|
||||
struct pci_devinfo *dinfo = pci_readcfg(&probe);
|
||||
if (dinfo != NULL) {
|
||||
if (dinfo->cfg.mfdev)
|
||||
pcifunchigh = 7;
|
||||
/*
|
||||
* XXX: Temporarily move pci_addcfg() up before
|
||||
@ -465,12 +515,13 @@ pci_probebus(int bus)
|
||||
* pci_addcfg() will then be moved back down
|
||||
* below the conditional statement ...
|
||||
*/
|
||||
pci_addcfg(cfg);
|
||||
pci_addcfg(dinfo);
|
||||
|
||||
if (bushigh < cfg->subordinatebus)
|
||||
bushigh = cfg->subordinatebus;
|
||||
if (bushigh < dinfo->cfg.subordinatebus)
|
||||
bushigh = dinfo->cfg.subordinatebus;
|
||||
|
||||
cfg = NULL; /* we don't own this anymore ... */
|
||||
/* XXX KDM */
|
||||
/* cfg = NULL; we don't own this anymore ... */
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -485,6 +536,8 @@ pci_probe(pciattach *parent)
|
||||
int bushigh;
|
||||
int bus = 0;
|
||||
|
||||
STAILQ_INIT(&pci_devq);
|
||||
|
||||
bushigh = pci_bushigh();
|
||||
while (bus <= bushigh) {
|
||||
int newbushigh;
|
||||
@ -518,33 +571,301 @@ pci_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 int
|
||||
pci_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 int
|
||||
pci_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
|
||||
{
|
||||
struct pci_io *io;
|
||||
int error;
|
||||
|
||||
if (cmd != PCIOCGETCONF && !(flag & FWRITE))
|
||||
if (!(flag & FWRITE))
|
||||
return EPERM;
|
||||
|
||||
|
||||
switch(cmd) {
|
||||
case PCIOCGETCONF:
|
||||
#ifdef NOTYET
|
||||
static struct pci_conf *pci_dev_list;
|
||||
static unsigned pci_dev_list_count;
|
||||
static unsigned pci_dev_list_size;
|
||||
{
|
||||
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;
|
||||
iolen = min(cio->pci_len,
|
||||
pci_dev_list_count * sizeof(struct pci_conf));
|
||||
cio->pci_len = pci_dev_list_count * sizeof(struct pci_conf);
|
||||
|
||||
error = copyout(pci_dev_list, cio->pci_buf, iolen);
|
||||
#else
|
||||
error = ENODEV;
|
||||
#endif
|
||||
break;
|
||||
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 %#lx, "
|
||||
"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 %#lx, 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) {
|
||||
|
@ -23,7 +23,7 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: pci_compat.c,v 1.9 1998/08/07 08:20:36 dfr Exp $
|
||||
* $Id: pci_compat.c,v 1.10 1998/09/06 22:41:42 tegge Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
@ -348,10 +348,12 @@ pci_drvmessage(pcicfgregs *cfg, char *name, int unit)
|
||||
|
||||
|
||||
void
|
||||
pci_drvattach(pcicfgregs *cfg)
|
||||
pci_drvattach(struct pci_devinfo *dinfo)
|
||||
{
|
||||
struct pci_device *dvp;
|
||||
pcicfgregs *cfg;
|
||||
|
||||
cfg = &dinfo->cfg;
|
||||
dvp = pci_finddrv(cfg);
|
||||
if (dvp != NULL) {
|
||||
int unit;
|
||||
@ -364,6 +366,23 @@ pci_drvattach(pcicfgregs *cfg)
|
||||
pci_drvmessage(cfg, dvp->pd_name, unit);
|
||||
if (dvp->pd_attach)
|
||||
dvp->pd_attach(cfg, unit);
|
||||
|
||||
dinfo->device = dvp;
|
||||
|
||||
/*
|
||||
* XXX KDM for some devices, dvp->pd_name winds up NULL.
|
||||
* I haven't investigated enough to figure out why this
|
||||
* would happen.
|
||||
*/
|
||||
if (dvp->pd_name != NULL)
|
||||
strncpy(dinfo->conf.pd_name, dvp->pd_name,
|
||||
sizeof(dinfo->conf.pd_name));
|
||||
else
|
||||
strncpy(dinfo->conf.pd_name, "????",
|
||||
sizeof(dinfo->conf.pd_name));
|
||||
|
||||
dinfo->conf.pd_unit = unit;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,25 +3,113 @@
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
|
||||
|
||||
#define PCI_MAXNAMELEN 16 /* max no. of characters in a device name */
|
||||
|
||||
typedef enum {
|
||||
PCI_GETCONF_LAST_DEVICE,
|
||||
PCI_GETCONF_LIST_CHANGED,
|
||||
PCI_GETCONF_MORE_DEVS,
|
||||
PCI_GETCONF_ERROR
|
||||
} pci_getconf_status;
|
||||
|
||||
typedef enum {
|
||||
PCI_GETCONF_NO_MATCH = 0x00,
|
||||
PCI_GETCONF_MATCH_BUS = 0x01,
|
||||
PCI_GETCONF_MATCH_DEV = 0x02,
|
||||
PCI_GETCONF_MATCH_FUNC = 0x04,
|
||||
PCI_GETCONF_MATCH_NAME = 0x08,
|
||||
PCI_GETCONF_MATCH_UNIT = 0x10,
|
||||
PCI_GETCONF_MATCH_VENDOR = 0x20,
|
||||
PCI_GETCONF_MATCH_DEVICE = 0x40,
|
||||
PCI_GETCONF_MATCH_CLASS = 0x80
|
||||
} pci_getconf_flags;
|
||||
|
||||
struct pcisel {
|
||||
u_char pc_bus; /* bus number */
|
||||
u_char pc_dev; /* device on this bus */
|
||||
u_char pc_func; /* function on this device */
|
||||
u_int8_t pc_bus; /* bus number */
|
||||
u_int8_t pc_dev; /* device on this bus */
|
||||
u_int8_t pc_func; /* function on this device */
|
||||
};
|
||||
|
||||
struct pci_conf {
|
||||
struct pcisel pc_sel; /* bus+slot+function */
|
||||
u_char pc_hdr; /* PCI header type */
|
||||
pcidi_t pc_devid; /* device ID */
|
||||
pcidi_t pc_subid; /* subvendor ID */
|
||||
u_int32_t pc_class; /* device class */
|
||||
struct pci_device *pc_dvp; /* device driver pointer or NULL */
|
||||
struct pcicb *pc_cb; /* pointer to bus parameters */
|
||||
u_int8_t pc_hdr; /* PCI header type */
|
||||
u_int16_t pc_subvendor; /* card vendor ID */
|
||||
u_int16_t pc_subdevice; /* card device ID, assigned by
|
||||
card vendor */
|
||||
u_int16_t pc_vendor; /* chip vendor ID */
|
||||
u_int16_t pc_device; /* chip device ID, assigned by
|
||||
chip vendor */
|
||||
u_int8_t pc_class; /* chip PCI class */
|
||||
u_int8_t pc_subclass; /* chip PCI subclass */
|
||||
u_int8_t pc_progif; /* chip PCI programming interface */
|
||||
u_int8_t pc_revid; /* chip revision ID */
|
||||
char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral
|
||||
device */
|
||||
u_long pd_unit; /* Unit number */
|
||||
};
|
||||
|
||||
struct pci_match_conf {
|
||||
struct pcisel pc_sel; /* bus+slot+function */
|
||||
char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral
|
||||
device */
|
||||
u_long pd_unit; /* Unit number */
|
||||
u_int16_t pc_vendor; /* PCI Vendor ID */
|
||||
u_int16_t pc_device; /* PCI Device ID */
|
||||
u_int8_t pc_class; /* PCI class */
|
||||
pci_getconf_flags flags; /* Matching expression */
|
||||
};
|
||||
|
||||
struct pci_conf_io {
|
||||
size_t pci_len; /* length of buffer */
|
||||
struct pci_conf *pci_buf; /* buffer */
|
||||
u_int32_t pat_buf_len; /*
|
||||
* Length of buffer passed in from
|
||||
* user space.
|
||||
*/
|
||||
u_int32_t num_patterns; /*
|
||||
* Number of pci_match_conf structures
|
||||
* passed in by the user.
|
||||
*/
|
||||
struct pci_match_conf *patterns; /*
|
||||
* Patterns passed in by the user.
|
||||
*/
|
||||
u_int32_t match_buf_len;/*
|
||||
* Length of match buffer passed
|
||||
* in by the user.
|
||||
*/
|
||||
u_int32_t num_matches; /*
|
||||
* Number of matches returned by
|
||||
* the kernel.
|
||||
*/
|
||||
struct pci_conf *matches; /*
|
||||
* PCI device matches returned by
|
||||
* the kernel.
|
||||
*/
|
||||
u_int32_t offset; /*
|
||||
* Passed in by the user code to
|
||||
* indicate where the kernel should
|
||||
* start traversing the device list.
|
||||
* The value passed out by the kernel
|
||||
* points to the record immediately
|
||||
* after the last one returned.
|
||||
* i.e. this value may be passed back
|
||||
* unchanged by the user for a
|
||||
* subsequent call.
|
||||
*/
|
||||
u_int32_t generation; /*
|
||||
* PCI configuration generation.
|
||||
* This only needs to be set if the
|
||||
* offset is set. The kernel will
|
||||
* compare its current generation
|
||||
* number to the generation passed
|
||||
* in by the user to determine
|
||||
* whether the PCI device list has
|
||||
* changed since the user last
|
||||
* called the GETCONF ioctl.
|
||||
*/
|
||||
pci_getconf_status status; /*
|
||||
* Status passed back from the
|
||||
* kernel.
|
||||
*/
|
||||
};
|
||||
|
||||
struct pci_io {
|
||||
|
@ -1,6 +1,3 @@
|
||||
#ifndef PCI_COMPAT
|
||||
#define PCI_COMPAT
|
||||
#endif
|
||||
/*
|
||||
* Copyright (c) 1997, Stefan Esser <se@freebsd.org>
|
||||
* All rights reserved.
|
||||
@ -26,10 +23,20 @@
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* $Id: pcivar.h,v 1.19 1998/07/22 08:39:08 dfr Exp $
|
||||
* $Id: pcivar.h,v 1.20 1998/08/13 19:12:20 gibbs Exp $
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _PCIVAR_H_
|
||||
#define _PCIVAR_H_
|
||||
|
||||
#ifndef PCI_COMPAT
|
||||
#define PCI_COMPAT
|
||||
#endif
|
||||
|
||||
#include <pci/pci_ioctl.h> /* XXX KDM */
|
||||
#include <sys/queue.h>
|
||||
|
||||
/* some PCI bus constants */
|
||||
|
||||
#define PCI_BUSMAX 255 /* highest supported bus number */
|
||||
@ -65,8 +72,6 @@ typedef struct {
|
||||
/* config header information common to all header types */
|
||||
|
||||
typedef struct pcicfg {
|
||||
struct pcicfg *parent;
|
||||
struct pcicfg *next;
|
||||
pcimap *map; /* pointer to array of PCI maps */
|
||||
void *hdrspec; /* pointer to header type specific data */
|
||||
|
||||
@ -153,10 +158,20 @@ typedef struct pciattach {
|
||||
struct pciattach *next;
|
||||
} pciattach;
|
||||
|
||||
struct pci_devinfo {
|
||||
STAILQ_ENTRY(pci_devinfo) pci_links;
|
||||
struct pci_device *device; /* should this be ifdefed? */
|
||||
pcicfgregs cfg;
|
||||
struct pci_conf conf;
|
||||
};
|
||||
|
||||
extern u_int32_t pci_numdevs;
|
||||
|
||||
|
||||
/* externally visible functions */
|
||||
|
||||
int pci_probe (pciattach *attach);
|
||||
void pci_drvattach(pcicfgregs *cfg);
|
||||
void pci_drvattach(struct pci_devinfo *dinfo);
|
||||
|
||||
/* low level PCI config register functions provided by pcibus.c */
|
||||
|
||||
@ -208,3 +223,4 @@ int pci_unmap_int (pcici_t tag);
|
||||
int pci_register_lkm (struct pci_device *dvp, int if_revision);
|
||||
|
||||
#endif /* PCI_COMPAT */
|
||||
#endif /* _PCIVAR_H_ */
|
||||
|
110
sys/sys/pciio.h
110
sys/sys/pciio.h
@ -3,25 +3,113 @@
|
||||
|
||||
#include <sys/ioccom.h>
|
||||
|
||||
|
||||
#define PCI_MAXNAMELEN 16 /* max no. of characters in a device name */
|
||||
|
||||
typedef enum {
|
||||
PCI_GETCONF_LAST_DEVICE,
|
||||
PCI_GETCONF_LIST_CHANGED,
|
||||
PCI_GETCONF_MORE_DEVS,
|
||||
PCI_GETCONF_ERROR
|
||||
} pci_getconf_status;
|
||||
|
||||
typedef enum {
|
||||
PCI_GETCONF_NO_MATCH = 0x00,
|
||||
PCI_GETCONF_MATCH_BUS = 0x01,
|
||||
PCI_GETCONF_MATCH_DEV = 0x02,
|
||||
PCI_GETCONF_MATCH_FUNC = 0x04,
|
||||
PCI_GETCONF_MATCH_NAME = 0x08,
|
||||
PCI_GETCONF_MATCH_UNIT = 0x10,
|
||||
PCI_GETCONF_MATCH_VENDOR = 0x20,
|
||||
PCI_GETCONF_MATCH_DEVICE = 0x40,
|
||||
PCI_GETCONF_MATCH_CLASS = 0x80
|
||||
} pci_getconf_flags;
|
||||
|
||||
struct pcisel {
|
||||
u_char pc_bus; /* bus number */
|
||||
u_char pc_dev; /* device on this bus */
|
||||
u_char pc_func; /* function on this device */
|
||||
u_int8_t pc_bus; /* bus number */
|
||||
u_int8_t pc_dev; /* device on this bus */
|
||||
u_int8_t pc_func; /* function on this device */
|
||||
};
|
||||
|
||||
struct pci_conf {
|
||||
struct pcisel pc_sel; /* bus+slot+function */
|
||||
u_char pc_hdr; /* PCI header type */
|
||||
pcidi_t pc_devid; /* device ID */
|
||||
pcidi_t pc_subid; /* subvendor ID */
|
||||
u_int32_t pc_class; /* device class */
|
||||
struct pci_device *pc_dvp; /* device driver pointer or NULL */
|
||||
struct pcicb *pc_cb; /* pointer to bus parameters */
|
||||
u_int8_t pc_hdr; /* PCI header type */
|
||||
u_int16_t pc_subvendor; /* card vendor ID */
|
||||
u_int16_t pc_subdevice; /* card device ID, assigned by
|
||||
card vendor */
|
||||
u_int16_t pc_vendor; /* chip vendor ID */
|
||||
u_int16_t pc_device; /* chip device ID, assigned by
|
||||
chip vendor */
|
||||
u_int8_t pc_class; /* chip PCI class */
|
||||
u_int8_t pc_subclass; /* chip PCI subclass */
|
||||
u_int8_t pc_progif; /* chip PCI programming interface */
|
||||
u_int8_t pc_revid; /* chip revision ID */
|
||||
char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral
|
||||
device */
|
||||
u_long pd_unit; /* Unit number */
|
||||
};
|
||||
|
||||
struct pci_match_conf {
|
||||
struct pcisel pc_sel; /* bus+slot+function */
|
||||
char pd_name[PCI_MAXNAMELEN + 1]; /* Name of peripheral
|
||||
device */
|
||||
u_long pd_unit; /* Unit number */
|
||||
u_int16_t pc_vendor; /* PCI Vendor ID */
|
||||
u_int16_t pc_device; /* PCI Device ID */
|
||||
u_int8_t pc_class; /* PCI class */
|
||||
pci_getconf_flags flags; /* Matching expression */
|
||||
};
|
||||
|
||||
struct pci_conf_io {
|
||||
size_t pci_len; /* length of buffer */
|
||||
struct pci_conf *pci_buf; /* buffer */
|
||||
u_int32_t pat_buf_len; /*
|
||||
* Length of buffer passed in from
|
||||
* user space.
|
||||
*/
|
||||
u_int32_t num_patterns; /*
|
||||
* Number of pci_match_conf structures
|
||||
* passed in by the user.
|
||||
*/
|
||||
struct pci_match_conf *patterns; /*
|
||||
* Patterns passed in by the user.
|
||||
*/
|
||||
u_int32_t match_buf_len;/*
|
||||
* Length of match buffer passed
|
||||
* in by the user.
|
||||
*/
|
||||
u_int32_t num_matches; /*
|
||||
* Number of matches returned by
|
||||
* the kernel.
|
||||
*/
|
||||
struct pci_conf *matches; /*
|
||||
* PCI device matches returned by
|
||||
* the kernel.
|
||||
*/
|
||||
u_int32_t offset; /*
|
||||
* Passed in by the user code to
|
||||
* indicate where the kernel should
|
||||
* start traversing the device list.
|
||||
* The value passed out by the kernel
|
||||
* points to the record immediately
|
||||
* after the last one returned.
|
||||
* i.e. this value may be passed back
|
||||
* unchanged by the user for a
|
||||
* subsequent call.
|
||||
*/
|
||||
u_int32_t generation; /*
|
||||
* PCI configuration generation.
|
||||
* This only needs to be set if the
|
||||
* offset is set. The kernel will
|
||||
* compare its current generation
|
||||
* number to the generation passed
|
||||
* in by the user to determine
|
||||
* whether the PCI device list has
|
||||
* changed since the user last
|
||||
* called the GETCONF ioctl.
|
||||
*/
|
||||
pci_getconf_status status; /*
|
||||
* Status passed back from the
|
||||
* kernel.
|
||||
*/
|
||||
};
|
||||
|
||||
struct pci_io {
|
||||
|
@ -29,7 +29,7 @@
|
||||
|
||||
#ifndef lint
|
||||
static const char rcsid[] =
|
||||
"$Id$";
|
||||
"$Id: pciconf.c,v 1.5 1997/10/06 11:38:30 charnier Exp $";
|
||||
#endif /* not lint */
|
||||
|
||||
#include <sys/types.h>
|
||||
@ -135,24 +135,54 @@ list_devs(void)
|
||||
struct pci_conf_io pc;
|
||||
struct pci_conf conf[255], *p;
|
||||
|
||||
fd = open(_PATH_DEVPCI, O_RDONLY, 0);
|
||||
fd = open(_PATH_DEVPCI, O_RDWR, 0);
|
||||
if (fd < 0)
|
||||
err(1, "%s", _PATH_DEVPCI);
|
||||
|
||||
pc.pci_len = sizeof(conf);
|
||||
pc.pci_buf = conf;
|
||||
bzero(&pc, sizeof(struct pci_conf_io));
|
||||
pc.match_buf_len = sizeof(conf);
|
||||
pc.matches = conf;
|
||||
|
||||
if (ioctl(fd, PCIOCGETCONF, &pc) < 0)
|
||||
err(1, "ioctl(PCIOCGETCONF)");
|
||||
do {
|
||||
if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
|
||||
err(1, "ioctl(PCIOCGETCONF)");
|
||||
|
||||
/*
|
||||
* 255 entries should be more than enough for most people,
|
||||
* but if someone has more devices, and then changes things
|
||||
* around between ioctls, we'll do the cheezy thing and
|
||||
* just bail. The alternative would be to go back to the
|
||||
* beginning of the list, and print things twice, which may
|
||||
* not be desireable.
|
||||
*/
|
||||
if (pc.status == PCI_GETCONF_LIST_CHANGED) {
|
||||
warnx("PCI device list changed, please try again");
|
||||
exitstatus = 1;
|
||||
close(fd);
|
||||
return;
|
||||
} else if (pc.status == PCI_GETCONF_ERROR) {
|
||||
warnx("Error returned from PCIOCGETCONF ioctl");
|
||||
exitstatus = 1;
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
for (p = conf; p < &conf[pc.num_matches]; p++) {
|
||||
if ((p->pd_name == NULL) || (*p->pd_name == '\0'))
|
||||
continue;
|
||||
|
||||
printf("%s%d@pci%d:%d:%d:\tclass=0x%06x card=0x%08lx "
|
||||
"chip=0x%08lx rev=0x%02x hdr=0x%02x\n",
|
||||
p->pd_name, p->pd_unit,
|
||||
p->pc_sel.pc_bus, p->pc_sel.pc_dev,
|
||||
p->pc_sel.pc_func, (p->pc_class << 16) |
|
||||
(p->pc_subclass << 8) | p->pc_progif,
|
||||
(p->pc_subdevice << 16) | p->pc_subvendor,
|
||||
(p->pc_device << 16) | p->pc_vendor,
|
||||
p->pc_revid, p->pc_hdr);
|
||||
}
|
||||
} while (pc.status == PCI_GETCONF_MORE_DEVS);
|
||||
|
||||
close(fd);
|
||||
|
||||
for (p = conf; p < &conf[pc.pci_len / sizeof conf[0]]; p++) {
|
||||
printf("pci%d:%d:%d:\tclass=0x%06x card=0x%08lx chip=0x%08lx rev=0x%02x hdr=0x%02x\n",
|
||||
p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func,
|
||||
p->pc_class >> 8, p->pc_subid,
|
||||
p->pc_devid, p->pc_class & 0xff, p->pc_hdr);
|
||||
}
|
||||
}
|
||||
|
||||
static struct pcisel
|
||||
|
Loading…
Reference in New Issue
Block a user