⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pciregions.c

📁 本源码是将述嵌入式LINUX驱动程序例程
💻 C
字号:
/*
 * pciregions.c --  a module that prints address regions of PCI devices
 *
 * $Id: pciregions.c,v 1.9 2001/03/16 11:30:23 rubini Exp $
 *
 * Copyright (C) 1997,2001   rubini@linux.it (Alessandro Rubini)
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
 */


#ifndef __KERNEL__
#  define __KERNEL__
#endif
#ifndef MODULE
#  define MODULE
#endif

#include <linux/config.h>
#include <linux/module.h>

#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/pci.h>

#include "sysdep.h"

static u32 addresses[] = {
    PCI_BASE_ADDRESS_0,
    PCI_BASE_ADDRESS_1,
    PCI_BASE_ADDRESS_2,
    PCI_BASE_ADDRESS_3,
    PCI_BASE_ADDRESS_4,
    PCI_BASE_ADDRESS_5,
    0
};

int pciregions_read_proc(char *buf, char **start, off_t offset,
                   int len, int *eof, void *data)
{
    int i, pos=0;
    int bus, devfn, is_multi = 0;
    unsigned char headertype, pribus, secbus;
    u16 vendorid, deviceid = 0;

    /* to print information about several buses, keep an array of them */
    #define MAXNBUS 8
    int buses[MAXNBUS] = {0,};
    int lastbus = 0; /* only one bus, by default, bus 0 */
    int busindex = 0;

    /* this macro helps keeping the following lines short */
    #define PRINTF(fmt, args...) sprintf(buf+len, fmt, ## args)
    len=0;

    /* Loop through the devices (code not printed in the book) */

    if (!pci_present())
        return sprintf(buf, "PCI not available in this computer\n");

    bus = buses[busindex]; /* first bus (bus 0) */
    for (devfn=0; pos < PAGE_SIZE; devfn++) {
	struct pci_dev *dev = NULL;
	/*
	 * A clean implementation
	 * would have a separate function to dump a single bus, but i
	 * preferred to keep it in one function to include part of it
	 * in the book (the printed code is automagically extracted from
	 * this file).
	 *
	 * Instead, I use a dirty trick to fold two loops in one.
	 */
	if (devfn > 0xff) { /* end of this bus */
	    if (busindex == lastbus) break; 
	    /* loop over to the next bus */
	    bus = buses[++busindex];
	    devfn = 0;
	}

	/*
	 * This code is derived from "drivers/pci/pci.c" in version
	 * 2.0, although it has been modified to work with the 2.4 interface.
	 * This means that the GPL applies to this source file
	 * and credit is due to the original authors
	 * (Drew Eckhardt, Frederic Potter, David Mosberger-Tang)
	 */

        if (PCI_FUNC(devfn) && !is_multi) /* not multi-function */
	    continue;

	dev = pci_find_slot(bus, devfn);
	if (!dev) {
	    if (!PCI_FUNC(devfn)) is_multi = 0; /* no first implies no other */
	    continue; /* no such device */
	}
	pci_read_config_byte(dev, PCI_HEADER_TYPE, &headertype);
        if (!PCI_FUNC(devfn)) /* first function */
	    is_multi = headertype & 0x80;
	headertype &= 0x7f; /* mask multi-function bit */

	/* FIXME: should get rid of the PAGE_SIZE limit */
        if (len > PAGE_SIZE / 2) { /* a big margin, just to be sure */
	    *eof = 1; return len;
	}
	vendorid = dev->vendor;
	deviceid = dev->device;

        len += PRINTF("Bus %i, device %2i, devfn %2i (id %04x-%04x,"
		      " headertype 0x%02x)\n",
		      bus, devfn>>3, devfn & 7,
		      vendorid, deviceid, headertype);
	if (headertype == PCI_HEADER_TYPE_BRIDGE) {
	    /* This is a bridge, print what it does */
	    pci_read_config_byte(dev, PCI_PRIMARY_BUS, &pribus);
	    pci_read_config_byte(dev, PCI_SECONDARY_BUS, &secbus);
	    len += PRINTF("\tbridge connecting PCI bus %i to PCI bus %i\n",
			  secbus, pribus);
	    /* remember about this bus, to dump it later */
	    if (lastbus <= MAXNBUS-1) {
		lastbus++;
		buses[lastbus] = secbus;
		len += PRINTF("\t(bus %i is dumped below)\n", secbus);
	    } else {
		len += PRINTF("\t(bus %i won't be dumped)\n", secbus);
	    }
	    pci_release_device(dev); /* 2.0 compatibility */
	    continue;
	} else if (headertype == PCI_HEADER_TYPE_CARDBUS) {
	    /* This is a CardBus bridge, print what it does */
	    pci_read_config_byte(dev, PCI_CB_PRIMARY_BUS,&pribus);
	    pci_read_config_byte(dev, PCI_CB_CARD_BUS,&secbus);
	    len += PRINTF("\tbridge connecting CardBus %i to PCI bus %i\n",
			  secbus, pribus);
	    pci_release_device(dev); /* 2.0 compatibility */
	    continue;
	} else if (headertype != PCI_HEADER_TYPE_NORMAL) {
	    len += PRINTF("\tunknown header type, skipping\n");
	    pci_release_device(dev); /* 2.0 compatibility */
	    continue;
	}
		
        /* Print the address regions of this device */
        for (i=0; addresses[i]; i++) {
            u32 curr, mask, size;
            char *type;

            pci_read_config_dword(dev, addresses[i],&curr);
            cli();
            pci_write_config_dword(dev, addresses[i],~0);
            pci_read_config_dword(dev, addresses[i],&mask);
            pci_write_config_dword(dev, addresses[i],curr);
            sti();

            if (!mask)
		continue; /* there may be other regions */

	    /*
	     * apply the I/O or memory mask to current position
	     * note that I/O is limited to 0xffff, and 64-bit is not
	     * supported by this simple imeplementation
	     */
	    if (curr & PCI_BASE_ADDRESS_SPACE_IO) {
		curr &= PCI_BASE_ADDRESS_IO_MASK;
	    } else {
		curr &= PCI_BASE_ADDRESS_MEM_MASK;
	    }
		
            len += PRINTF("\tregion %i: mask 0x%08lx, now at 0x%08lx\n", i,
                           (unsigned long)mask,
                           (unsigned long)curr);
            /* extract the type, and the programmable bits */
            if (mask & PCI_BASE_ADDRESS_SPACE_IO) {
                type = "I/O"; mask &= PCI_BASE_ADDRESS_IO_MASK;
		size = (~mask + 1) & 0xffff; /* Bleah */
            } else {
                type = "mem"; mask &= PCI_BASE_ADDRESS_MEM_MASK;
		size = ~mask + 1;
            }
            len += PRINTF("\tregion %i: type %s, size %i (%i%s)\n", i,
                          type, size,
			  (size & 0xfffff) == 0 ? size >> 20 :
			    (size & 0x3ff) == 0 ? size >> 10 : size,
			  (size & 0xfffff) == 0 ? "MB" :
			    (size & 0x3ff) == 0 ? "KB" : "B");
	    if (len > PAGE_SIZE / 2) {
		len += PRINTF("... more info skipped ...\n");
		*eof = 1; return len;
	    }
        }
	pci_release_device(dev); /* 2.0 compatibility */
    } /* devfn */
    *eof = 1;
    return len;
}

#ifdef USE_PROC_REGISTER

static int pciregions_old_read_proc(char *buf, char **start, off_t offset,
				 int len, int unused)
{
    int eof;
    return pciregions_read_proc(buf, start, offset, len, &eof, NULL);
}


struct proc_dir_entry pciregions_proc_entry = {
        0,                 /* low_ino: the inode -- dynamic */
       10, "pciregions",   /* len of name and name */
        S_IFREG | S_IRUGO, /* mode */
        1, 0, 0,           /* nlinks, owner, group */
        0, NULL,           /* size - unused; operations -- use default */
        &pciregions_old_read_proc,   /* function used to read data */
        /* nothing more */
    };

#endif /* USE_PROC_REGISTER */


int init_module(void)
{
#ifdef USE_PROC_REGISTER
    proc_register_dynamic(&proc_root, &pciregions_proc_entry);
#else
    create_proc_read_entry("pciregions", 0, NULL, pciregions_read_proc, NULL);
#endif
    return 0;
}

void cleanup_module(void)
{
#ifdef USE_PROC_REGISTER
    proc_unregister(&proc_root, pciregions_proc_entry.low_ino);
#else
    remove_proc_entry("pciregions", 0);
#endif
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -