patch-2.4.19 linux-2.4.19/arch/ia64/sn/io/pciba.c

Next file: linux-2.4.19/arch/ia64/sn/io/pcibr.c
Previous file: linux-2.4.19/arch/ia64/sn/io/pci_dma.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/arch/ia64/sn/io/pciba.c linux-2.4.19/arch/ia64/sn/io/pciba.c
@@ -1,1716 +1,958 @@
-/* $Id$
+/*
+ * arch/ia64/sn/io/pciba.c
  *
- * This file is subject to the terms and conditions of the GNU General Public
- * License.  See the file "COPYING" in the main directory of this archive
- * for more details.
+ * IRIX PCIBA-inspired user mode PCI interface
  *
- * Copyright (C) 1992 - 1997, 2000 Silicon Graphics, Inc.
- * Copyright (C) 2000 by Colin Ngam
+ * requires: devfs
+ *
+ * device nodes show up in /dev/pci/BB/SS.F (where BB is the bus the
+ * device is on, SS is the slot the device is in, and F is the
+ * device's function on a multi-function card).
+ *
+ * when compiled into the kernel, it will only be initialized by the
+ * sgi sn1 specific initialization code.  in this case, device nodes
+ * are under /dev/hw/..../
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License.  See the file "COPYING" in the main directory of
+ * this archive for more details.
+ *
+ * Copyright (C) 2001-2002 Silicon Graphics, Inc.  All rights reserved.
+ *
+ * 03262001 - Initial version by Chad Talbott
  */
 
-#include <linux/types.h>
-#include <linux/config.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <asm/sn/sgi.h>
-#include <asm/sn/addrs.h>
-#include <asm/sn/arch.h>
-#include <asm/sn/iograph.h>
-#include <asm/sn/invent.h>
-#include <asm/sn/hcl.h>
-#include <asm/sn/labelcl.h>
-#include <asm/sn/xtalk/xwidget.h>
-#include <asm/sn/pci/bridge.h>
-#include <asm/sn/pci/pciio.h>
-#include <asm/sn/pci/pcibr.h>
-#include <asm/sn/pci/pcibr_private.h>
-#include <asm/sn/pci/pci_defs.h>
-#include <asm/sn/prio.h>
-#include <asm/sn/ioerror_handling.h>
-#include <asm/sn/xtalk/xbow.h>
-#include <asm/sn/ioc3.h>
-#include <asm/sn/eeprom.h>
-#include <asm/sn/sn1/bedrock.h>
-#include <asm/sn/sn_private.h>
-#if defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)
-#include <asm/sn/sn1/hubio.h>
-#include <asm/sn/sn1/hubio_next.h>
-#endif
 
-#define copyin(_a, _b, _c)	copy_from_user(_b, _a, _c)
+/* jesse's beefs:
+
+   register_pci_device should be documented
+   
+   grossness with do_swap should be documented
+   
+   big, gross union'ized node_data should be replaced with independent
+   structures
+
+   replace global list of nodes with global lists of resources.  could
+   use object oriented approach of allocating and cleaning up
+   resources.
+   
+*/
+
 
-#ifndef	DEBUG_PCIBA
-#define	DEBUG_PCIBA		0
+#include <linux/config.h>
+#ifndef CONFIG_DEVFS_FS
+#  error PCIBA requires devfs
 #endif
 
-/* v_mapphys does not percolate page offset back. */
-#define	PCIBA_ALIGN_CHECK	1
+#include <linux/module.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/pci.h>
+#include <linux/list.h>
+
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mman.h>
+#include <linux/init.h>
+#include <linux/raw.h>
+#include <linux/capability.h>
+
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/pgalloc.h>
+#include <asm/page.h>
 
 #include <asm/sn/pci/pciba.h>
 
-/* grab an unused space code for "User DMA" space */
-#ifndef	PCIBA_SPACE_UDMA
-#define	PCIBA_SPACE_UDMA	(14)
+
+MODULE_DESCRIPTION("User mode PCI interface");
+MODULE_AUTHOR("Chad Talbott");
+
+
+#undef DEBUG_PCIBA
+/* #define DEBUG_PCIBA */
+
+#undef TRACE_PCIBA
+/* #define TRACE_PCIBA */
+
+#if defined(DEBUG_PCIBA)
+#  define DPRINTF(x...) printk(KERN_DEBUG x)
+#else
+#  define DPRINTF(x...)
 #endif
 
-#if DEBUG_REFCT
-extern int		hwgraph_vertex_refct(vertex_hdl_t);
+#if defined(TRACE_PCIBA)
+#  if defined(__GNUC__)
+#    define TRACE()	printk(KERN_DEBUG "%s:%d:%s\n", \
+			       __FILE__, __LINE__, __FUNCTION__)
+#  else
+#    define TRACE()	printk(KERN_DEBUG "%s:%d\n", __LINE__, __FILE__)
+#  endif
+#else
+#  define TRACE()
 #endif
-extern int		pci_user_dma_max_pages;
 
-#define NEW(ptr)	(ptr = kmem_zalloc(sizeof (*(ptr)), KM_SLEEP))
-#define DEL(ptr)	(kfree(ptr))
 
-/* Oops -- no standard "pci address" type! */
-typedef uint64_t        pciaddr_t;
+typedef enum { failure, success } status;
+typedef enum { false, true } boolean;
 
-/* ================================================================
- *            driver types
- */
-typedef struct pciba_slot_s *pciba_slot_t;
-typedef struct pciba_comm_s *pciba_comm_t;
-typedef struct pciba_soft_s *pciba_soft_t;
-typedef struct pciba_map_s *pciba_map_t, **pciba_map_h;
-typedef struct pciba_dma_s *pciba_dma_t, **pciba_dma_h;
-typedef struct pciba_bus_s *pciba_bus_t;
-
-#define	TRACKED_SPACES	16
-struct pciba_comm_s {
-    devfs_handle_t	    conn;
-    pciba_bus_t             bus;
-    int			    refct;
-    pciba_soft_t	    soft[TRACKED_SPACES][2];
-    struct semaphore        lock;
-    pciba_dma_t             dmap;
+
+/* major data structures:
+
+   struct node_data -
+   
+   	one for each file registered with devfs.  contains everything
+   	that any file's fops would need to know about.
+
+   struct dma_allocation -
+
+   	a single DMA allocation.  only the 'dma' nodes care about
+   	these.  they are there primarily to allow the driver to look
+   	up the kernel virtual address of dma buffers allocated by
+   	pci_alloc_consistent, as the application is only given the
+   	physical address (to program the device's dma, presumably) and
+   	cannot supply the kernel virtual address when freeing the
+   	buffer.
+
+	it's also useful to maintain a list of buffers allocated
+	through a specific node to allow some sanity checking by this
+	driver.  this prevents (for example) a broken application from
+	freeing buffers that it didn't allocate, or buffers allocated
+	on another node.
+   
+   global_node_list -
+
+   	a list of all nodes allocated.  this allows the driver to free
+   	all the memory it has 'kmalloc'd in case of an error, or on
+   	module removal.
+
+   global_dma_list -
+
+        a list of all dma buffers allocated by this driver.  this
+	allows the driver to 'pci_free_consistent' all buffers on
+	module removal or error.
+
+*/
+
+
+struct node_data {
+	/* flat list of all the device nodes.  makes it easy to free
+	   them all when we're unregistered */
+	struct list_head global_node_list;
+	devfs_handle_t devfs_handle;
+
+	void (* cleanup)(struct node_data *);
+
+	union {
+		struct {
+			struct pci_dev * dev;
+			struct list_head dma_allocs;
+			boolean mmapped;
+		} dma;
+		struct {
+			struct pci_dev * dev;
+			u32 saved_rom_base_reg;
+			boolean mmapped;
+		} rom;
+		struct {
+			struct resource * res;
+		} base;
+		struct {
+			struct pci_dev * dev;
+		} config;
+	} u;
 };
 
-/* pciba_soft: device_info() for all openables */
-struct pciba_soft_s {
-    pciba_comm_t	    comm;
-    devfs_handle_t          vhdl;
-    int			    refct;
-    pciio_space_t           space;
-    size_t                  size;
-    pciio_space_t           iomem;
-    pciaddr_t               base;
-    unsigned		    flags;
+struct dma_allocation {
+	struct list_head list;
+
+	dma_addr_t handle;
+	void * va;
+	size_t size;
 };
 
-#define	pciba_soft_get(v)	(pciba_soft_t)hwgraph_fastinfo_get(v)
-#define	pciba_soft_set(v,i)	hwgraph_fastinfo_set(v,(arbitrary_info_t)(i))
 
-#define	pciba_soft_lock(soft)	down(&soft->comm->lock)
-#define	pciba_soft_unlock(soft)	up(&soft->comm->lock)
+static LIST_HEAD(global_node_list);
+static LIST_HEAD(global_dma_list);
 
-/* pciba_map: data describing a mapping.
- * (ie. a user mmap request)
- */
-struct pciba_map_s {
-    pciba_map_t             next;
-#ifdef LATER
-    uthread_t              *uthread;
-#endif
-    __psunsigned_t          handle;
-    uvaddr_t                uvaddr;
-    size_t                  size;
-    pciio_piomap_t          map;
-    pciio_space_t           space;
-    pciaddr_t               base;
-    unsigned		    flags;
-};
 
-/* pciba_dma: data describing a DMA mapping.
- */
-struct pciba_dma_s {
-    pciba_dma_t             next;
-    iopaddr_t               paddr;	/* starting phys addr */
-    caddr_t                 kaddr;	/* starting kern addr */
-    pciio_dmamap_t          map;	/* mapping resources (ugh!) */
-    pciaddr_t               daddr;	/* starting pci addr */
-    size_t                  pages;	/* size of block in pages */
-    size_t                  bytes;	/* size of block in bytes */
-    __psunsigned_t          handle;	/* mapping handle */
+/* module entry points */
+int __init pciba_init(void);
+void __exit pciba_exit(void);
+
+static status __init register_with_devfs(void);
+static void __exit unregister_with_devfs(void);
+
+static status __init register_pci_device(devfs_handle_t device_dir_handle,
+					 struct pci_dev * dev);
+
+/* file operations */
+static int generic_open(struct inode * inode, struct file * file);
+static int rom_mmap(struct file * file, struct vm_area_struct * vma);
+static int rom_release(struct inode * inode, struct file * file);
+static int base_mmap(struct file * file, struct vm_area_struct * vma);
+static int config_ioctl(struct inode * inode, struct file * file, 
+			unsigned int cmd, 
+			unsigned long arg);
+static int dma_ioctl(struct inode * inode, struct file * file, 
+		     unsigned int cmd, 
+		     unsigned long arg);
+static int dma_mmap(struct file * file, struct vm_area_struct * vma);
+
+/* support routines */
+static int mmap_pci_address(struct vm_area_struct * vma, unsigned long pci_va);
+static int mmap_kernel_address(struct vm_area_struct * vma, void * kernel_va);
+
+#ifdef DEBUG_PCIBA
+static void dump_nodes(struct list_head * nodes);
+static void dump_allocations(struct list_head * dalp);
+#endif
+
+/* file operations for each type of node */
+static struct file_operations rom_fops = {
+	owner:		THIS_MODULE,
+	mmap:		rom_mmap,
+	open:		generic_open,
+	release:	rom_release
 };
+ 
 
-/* pciba_bus: common bus info for all openables
- * descended from the same master vertex.
- */
-struct pciba_bus_s {
-    struct semaphore        lock;
-    pciba_map_t             maps;	/* stack of mappings */
-    int			    refct;
+static struct file_operations base_fops = {
+	owner:		THIS_MODULE,
+	mmap:		base_mmap,
+	open:		generic_open
 };
 
-#define	pciba_bus_lock(bus)	down(&bus->lock)
-#define	pciba_bus_unlock(bus)	up(&bus->lock)
 
-typedef union ioctl_arg_buffer_u {
-    char                    data[IOCPARM_MASK + 1];
-    uint8_t                 uc;
-    uint16_t                us;
-    uint32_t                ui;
-    uint64_t                ud;
-    caddr_t                 ca;
-#if ULI
-    struct uliargs          uli;
-    struct uliargs32	    uli32;
-#endif
-} ioctl_arg_buffer_t;
+static struct file_operations config_fops = {
+	owner:		THIS_MODULE,
+	ioctl:		config_ioctl,
+	open:		generic_open
+};	
 
-/* ================================================================
- *            driver variables
- */
-char                   *pciba_mversion = "mload version 7.0";
-int                     pciba_devflag = 0x1 |
-                                        0x200 |
-                                        0x400;
+static struct file_operations dma_fops = {
+	owner:		THIS_MODULE,
+	ioctl:		dma_ioctl,
+	mmap:		dma_mmap,
+	open:		generic_open
+};	
 
-/* this counts the reasons why we can not
- * currently unload this driver.
- */
-atomic_t                pciba_prevent_unload = ATOMIC_INIT(0);
 
-#if DEBUG_PCIBA
-static struct reg_values space_v[] =
-{
-    {PCIIO_SPACE_NONE, "none"},
-    {PCIIO_SPACE_ROM, "ROM"},
-    {PCIIO_SPACE_IO, "I/O"},
-    {PCIIO_SPACE_MEM, "MEM"},
-    {PCIIO_SPACE_MEM32, "MEM(32)"},
-    {PCIIO_SPACE_MEM64, "MEM(64)"},
-    {PCIIO_SPACE_CFG, "CFG"},
-    {PCIIO_SPACE_WIN(0), "WIN(0)"},
-    {PCIIO_SPACE_WIN(1), "WIN(1)"},
-    {PCIIO_SPACE_WIN(2), "WIN(2)"},
-    {PCIIO_SPACE_WIN(3), "WIN(3)"},
-    {PCIIO_SPACE_WIN(4), "WIN(4)"},
-    {PCIIO_SPACE_WIN(5), "WIN(5)"},
-    {PCIBA_SPACE_UDMA, "UDMA"},
-    {PCIIO_SPACE_BAD, "BAD"},
-    {0}
-};
+module_init(pciba_init);
+module_exit(pciba_exit);
+
 
-static struct reg_desc  space_desc[] =
+int __init
+pciba_init(void)
 {
-    {0xFF, 0, "space", 0, space_v},
-    {0}
-};
-#endif
+	TRACE();
 
-char                    pciba_edge_lbl_base[] = "base";
-char                    pciba_edge_lbl_cfg[] = "config";
-char                    pciba_edge_lbl_dma[] = "dma";
-char                    pciba_edge_lbl_intr[] = "intr";
-char                    pciba_edge_lbl_io[] = "io";
-char                    pciba_edge_lbl_mem[] = "mem";
-char                    pciba_edge_lbl_rom[] = "rom";
-char                   *pciba_edge_lbl_win[6] =
-{"0", "1", "2", "3", "4", "5"};
-
-#define	PCIBA_EDGE_LBL_BASE	pciba_edge_lbl_base
-#define	PCIBA_EDGE_LBL_CFG	pciba_edge_lbl_cfg
-#define	PCIBA_EDGE_LBL_DMA	pciba_edge_lbl_dma
-#define	PCIBA_EDGE_LBL_INTR	pciba_edge_lbl_intr
-#define	PCIBA_EDGE_LBL_IO	pciba_edge_lbl_io
-#define	PCIBA_EDGE_LBL_MEM	pciba_edge_lbl_mem
-#define	PCIBA_EDGE_LBL_ROM	pciba_edge_lbl_rom
-#define	PCIBA_EDGE_LBL_WIN(n)	pciba_edge_lbl_win[n]
-
-#define	PCIBA_EDGE_LBL_FLIP	pciba_edge_lbl_flip
-
-static char             pciba_info_lbl_bus[] = "pciba_bus";
-
-#define	PCIBA_INFO_LBL_BUS	pciba_info_lbl_bus
-
-struct file_operations pciba_fops = {
-	owner:	THIS_MODULE,
-	llseek: NULL,
-	read: NULL,
-	write: NULL,
-	readdir: NULL,
-	poll: NULL,
-	ioctl: NULL,
-	mmap: NULL,
-	open: NULL,
-	flush: NULL,
-	release: NULL,
-	fsync: NULL,
-	fasync: NULL,
-	lock: NULL,
-	readv: NULL,
-	writev: NULL
-};
-	
-/* ================================================================
- *            function table of contents
- */
+	if (register_with_devfs() == failure)
+		return 1; /* failure */
 
-void                    pciba_init(void);
-int                     pciba_attach(devfs_handle_t);
+	printk("PCIBA (a user mode PCI interface) initialized.\n");
 
-static void		pciba_sub_attach(pciba_comm_t,
-					 pciio_space_t, pciio_space_t, pciaddr_t,
-					 devfs_handle_t, devfs_handle_t, char *);
-
-static pciba_bus_t      pciba_find_bus(devfs_handle_t, int);
-#ifdef LATER
-static void             pciba_map_push(pciba_bus_t, pciba_map_t);
-static pciba_map_t      pciba_map_pop_hdl(pciba_bus_t, __psunsigned_t);
-static void             pciba_sub_detach(devfs_handle_t, char *);
-static pciio_iter_f     pciba_unload_me;
-#endif
+	return 0; /* success */
+}
 
-int                     pciba_unload(void);
-int                     pciba_unreg(void);
-int                     pciba_detach(devfs_handle_t);
-
-int                     pciba_open(dev_t *, int, int, struct cred *);
-int                     pciba_close(dev_t);
-int                     pciba_read(dev_t, cred_t *);
-int                     pciba_write(dev_t, cred_t *);
-int                     pciba_ioctl(dev_t, int, void *, int, cred_t *, int *);
-
-int                     pciba_map(dev_t, vhandl_t *, off_t, size_t, uint32_t);
-int                     pciba_unmap(dev_t, vhandl_t *);
-
-#if ULI
-void                    pciba_clearuli(struct uli *);
-static intr_func_f      pciba_intr;
-#endif /* Undef as it gets implemented */
 
-/* ================================================================
- *            driver load, register, and setup
- */
-void
-pciba_init(void)
+void __exit
+pciba_exit(void)
 {
+	TRACE();
 
-	/*
-	 * What do we need to do here?
-	 */
-#if DEBUG_PCIBA
-    printk("pciba_init()\n");
-#endif
+	/* FIXME: should also free all that memory that we allocated
+           ;) */
+	unregister_with_devfs();
 }
 
-#ifdef LATER
-#if HWG_PERF_CHECK && IP30 && !DEBUG
-void
-pciba_timeout(void *arg1, void *arg2)
-{
-    struct semaphore       *semap = (sema_t *) arg1;
-    unsigned long          *cvalp = (unsigned long *) arg2;
-
-    if (cvalp)
-	cvalp[0] = RAW_COUNT();
-    if (semap)
-	up(semap);
-}
-
-volatile unsigned long  cNval[1];
-struct semaphore        tsema;
-
-void
-pciba_timeout_test(void)
-{
-    unsigned long           c0val, cval;
-    toid_t                  tid;
-
-    extern void             hwg_hprint(unsigned long, char *);
-
-    sema_init(&tsema, 0);
-
-    cNval[0] = 0;
-    c0val = RAW_COUNT();
-    tid = timeout((void (*)()) pciba_timeout, (void *) 0, 1, (void *) cNval);
-    DELAY(1000000);
-    cval = cNval[0];
-    if (cval == 0) {
-	untimeout(tid);
-	PRINT_ALERT("pciba: one-tick timeout did not happen in a second\n");
-	return;
-    }
-    cval = cval - c0val;
-    hwg_hprint(cval, "timeout(1)");
-
-    cNval[0] = 0;
-    c0val = RAW_COUNT();
-    tid = timeout((void (*)()) pciba_timeout, (void *) &tsema, 2, (void *) cNval);
-
-    /* FIXME : this probably needs to be down_interruptible() */
-
-    if (down(&tsema) < 0) {	/* wait for the pciba_timeout */
-	untimeout(tid);
-	PRINT_WARNING("pciba: timeout(2) time check aborted\n");
-	return;
-    }
-    cval = cNval[0];
-    if (cval == 0) {
-	untimeout(tid);
-	PRINT_WARNING("pciba: timeout(2) time not logged\n");
-	return;
-    }
-    cval = cval - c0val;
-    hwg_hprint(cval, "timeout(2)");
-
-    cNval[0] = 0;
-    c0val = RAW_COUNT();
-    tid = timeout((void (*)()) pciba_timeout, (void *) &tsema, HZ, (void *) cNval);
-
-    /* FIXME : this probably needs to be down_interruptible() */
-
-    if (down(&tsema) < 0) {	/* wait for the pciba_timeout */
-	untimeout(tid);
-	PRINT_WARNING("pciba: timeout(HZ) time check aborted\n");
-	return;
-    }
-    cval = cNval[0];
-    if (cval == 0) {
-	untimeout(tid);
-	PRINT_WARNING("pciba: timeout(HZ) time not logged\n");
-	return;
-    }
-    cval = cval - c0val;
-    hwg_hprint(cval, "timeout(HZ)");
-
-    printk("verifying untimeout() cancells ...\n");
-    cNval[0] = 0;
-    tid = timeout((void (*)()) pciba_timeout, (void *) 0, 2, (void *) cNval);
-    untimeout(tid);
-    DELAY(1000000);
-    cval = cNval[0];
-    if (cval != 0) {
-	PRINT_ALERT("pciba: unable to cancel two-tick timeout\n");
-	cval -= c0val;
-	hwg_hprint(cval, "CANCELLED timeout(2)");
-    }
-}
-#endif
 
-int
-pciba_reg(void)
+# if 0
+static void __exit
+free_nodes(void)
 {
-#if DEBUG_PCIBA
-    printk("pciba_reg()\n");
-#endif
-    pciio_driver_register(-1, -1, "pciba_", 0);
-
-#if HWG_PERF_CHECK && IP30 && !DEBUG
-    printk("%s %d\n", __FUNCTION__, __LINE__);
-pciba_timeout_test();
-#endif
+	struct node_data * nd;
+	
+	TRACE();
 
-#if DEBUG_REFCT
-    {
-	char	       *cname = "pciba";
-	char	       *dname = "ptv";
-	char           *cpath0 = "node/xtalk/15";
-	char           *uname0 = "0";
-	char           *cpath1 = "node/xtalk/13";
-	char           *uname1 = "1";
-	devfs_handle_t conn;
-	devfs_handle_t conv;
-	devfs_handle_t vhdl;
-	int		ret;
-
-	printk("pciba refct tests:\n");
-
-#define	SHOWREF(vhdl,func)	printk("ref=%d\t%s\t(%d) %v\n", hwgraph_vertex_refct(vhdl), #func, vhdl, vhdl);
-
-	if (GRAPH_SUCCESS != (ret = hwgraph_path_add(hwgraph_root, cname, &conv)))
-	    printk("\tunable to create conv (ret=%d)\n", ret);
-	else {						SHOWREF(conv, hwgraph_path_add);
-	    if (GRAPH_SUCCESS != (ret = hwgraph_traverse(hwgraph_root, cpath0, &conn)))
-		printk("\tunable to find %s (ret=%d)\n", cpath0, ret);
-	    else {					SHOWREF(conn, hwgraph_traverse);
-		if (GRAPH_SUCCESS != (ret = hwgraph_char_device_add(conn, dname, "pciba_", &vhdl)))
-		    printk("unable to create %v/%s (ret=%d)\n", conn, dname, ret);
-		else {					SHOWREF(vhdl, hwgraph_char_device_add);
-		    hwgraph_chmod(vhdl, 0666);		SHOWREF(vhdl, hwgraph_chmod);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_add(conv, vhdl, uname0)))
-			printk("unable to create %v/%s (ret=%d)\n", conn, uname0, vhdl, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_add);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(vhdl)))
-			printk("unable to unref %v\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_unref);
-		}
-		if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(conn)))
-		    printk("unable to unref %v\n", conn);
-		else					SHOWREF(conn, hwgraph_vertex_unref);
-	    }
- 
-	    if (GRAPH_SUCCESS != (ret = hwgraph_traverse(hwgraph_root, cpath1, &conn)))
-		printk("\tunable to find %s (ret=%d)\n", cpath1, ret);
-	    else {					SHOWREF(conn, hwgraph_traverse);
-		if (GRAPH_SUCCESS != (ret = hwgraph_char_device_add(conn, dname, "pciba_", &vhdl)))
-		    printk("unable to create %v/%s (ret=%d)\n", conn, dname, ret);
-		else {					SHOWREF(vhdl, hwgraph_char_device_add);
-		    hwgraph_chmod(vhdl, 0666);		SHOWREF(vhdl, hwgraph_chmod);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_add(conv, vhdl, uname1)))
-			printk("unable to create %v/%s (ret=%d)\n", conn, uname1, vhdl, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_add);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(vhdl)))
-			printk("unable to unref %v\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_unref);
-		}
-		if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(conn)))
-		    printk("unable to unref %v\n", conn);
-		else					SHOWREF(conn, hwgraph_vertex_unref);
-	    }
- 
-	    if (GRAPH_SUCCESS != (ret = hwgraph_traverse(hwgraph_root, cpath0, &conn)))
-		printk("\tunable to find %s (ret=%d)\n", cpath0, ret);
-	    else {					SHOWREF(conn, hwgraph_traverse);
-		if (GRAPH_SUCCESS != (ret = hwgraph_traverse(conn, dname, &vhdl)))
-		    printk("\tunable to find %v/%s (ret=%d)\n", conn, dname, ret);
-		else {					SHOWREF(vhdl, hwgraph_traverse);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_remove(conv, uname0, NULL)))
-			printk("\tunable to remove edge %v/%s (ret=%d)\n", conv, uname0, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_remove);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_remove(conn, dname, NULL)))
-			printk("\tunable to remove edge %v/%s (ret=%d)\n", conn, dname, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_remove);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(vhdl)))
-			printk("unable to unref %v\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_unref);
-		    if (GRAPH_SUCCESS == (ret = hwgraph_vertex_destroy(vhdl)))
-			printk("\tvertex %d destroyed OK\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_destroy);
-		}
-		if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(conn)))
-		    printk("unable to unref %v\n", conn);
-		else					SHOWREF(conn, hwgraph_vertex_unref);
-	    }
-
-	    if (GRAPH_SUCCESS != (ret = hwgraph_traverse(hwgraph_root, cpath1, &conn)))
-		printk("\tunable to find %s (ret=%d)\n", cpath1, ret);
-	    else {					SHOWREF(conn, hwgraph_traverse);
-		if (GRAPH_SUCCESS != (ret = hwgraph_traverse(conn, dname, &vhdl)))
-		    printk("\tunable to find %v/%s (ret=%d)\n", conn, dname, ret);
-		else {					SHOWREF(vhdl, hwgraph_traverse);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_remove(conv, uname1, NULL)))
-			printk("\tunable to remove edge %v/%s (ret=%d)\n", conv, uname1, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_remove);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_edge_remove(conn, dname, NULL)))
-			printk("\tunable to remove edge %v/%s (ret=%d)\n", conn, dname, ret);
-		    else				SHOWREF(vhdl, hwgraph_edge_remove);
-		    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(vhdl)))
-			printk("unable to unref %v\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_unref);
-		    if (GRAPH_SUCCESS == (ret = hwgraph_vertex_destroy(vhdl)))
-			printk("\tvertex %d destroyed OK\n", vhdl);
-		    else				SHOWREF(vhdl, hwgraph_vertex_destroy);
-		}
-		if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(conn)))
-		    printk("unable to unref %v\n", conn);
-		else					SHOWREF(conn, hwgraph_vertex_unref);
-	    }
-
-	    if (GRAPH_SUCCESS != (ret = hwgraph_edge_remove(hwgraph_root, cname, NULL)))
-		printk("\tunable to remove edge %v/%s (ret=%d)\n", hwgraph_root, cname, ret);
-	    else				SHOWREF(conv, hwgraph_edge_remove);
-	    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_unref(conv)))
-		printk("unable to unref %v\n", conv);
-	    else					SHOWREF(conv, hwgraph_vertex_unref);
-	    if (GRAPH_SUCCESS == (ret = hwgraph_vertex_destroy(conv)))
-		printk("\tvertex %d destroyed OK\n", conv);
-	    else					SHOWREF(conv, hwgraph_vertex_destroy);
+	list_for_each(nd, &node_list) {
+		kfree(list_entry(nd, struct nd, node_list));
 	}
-    }
-#endif
-
-    return 0;
 }
-
-#endif
-int
-pciba_attach(devfs_handle_t hconn)
-{
-#if defined(PCIIO_SLOT_NONE)
-    pciio_info_t            info = pciio_info_get(hconn);
-    pciio_slot_t            slot = pciio_info_slot_get(info);
 #endif
-    pciba_comm_t	    comm;
-    pciba_bus_t             bus;
-    int                     ht;
-    devfs_handle_t	    hbase;
-    devfs_handle_t          gconn;
-    devfs_handle_t          gbase;
-    int                     win;
-    int                     wins;
-    pciio_space_t           space;
-    pciaddr_t               base;
 
-    int                     iwins;
-    int                     mwins;
 
-#if DEBUG_PCIBA
-    printk("pciba_attach(%p)\n", hconn);
-#endif
+static devfs_handle_t pciba_devfs_handle;
 
-    /* Pick up "dualslot guest" vertex,
-     * which gets all functionality except
-     * config space access.
-     */
-    if ((GRAPH_SUCCESS !=
-	 hwgraph_traverse(hconn, ".guest", &gconn)) ||
-	(hconn == gconn))
-	gconn = GRAPH_VERTEX_NONE;
-
-    bus = pciba_find_bus(hconn, 1);
-    bus->refct ++;
-
-    /* set up data common to all pciba openables
-     * on this connection point.
-     */
-    NEW(comm);
-    comm->conn = hconn;
-    comm->bus = bus;
-    comm->refct = 0;
-    sema_init(&comm->lock, 1);
 
-#if !defined(PCIIO_SLOT_NONE)
-    if (bus->refct == 1)
-#else
-    if (slot == PCIIO_SLOT_NONE)
-#endif
-    {
-	pciio_info_t            pciio_info;
-	devfs_handle_t          master;
-
-	pciio_info = pciio_info_get(hconn);
-	master = pciio_info_master_get(pciio_info);
-
-	pciba_sub_attach(comm, PCIIO_SPACE_IO, PCIIO_SPACE_IO, 0, master, master, PCIBA_EDGE_LBL_IO);
-	pciba_sub_attach(comm, PCIIO_SPACE_MEM, PCIIO_SPACE_MEM, 0, master, master, PCIBA_EDGE_LBL_MEM);
-#if defined(PCIIO_SLOT_NONE)
-	return 0;
-#endif
-    }
+#if !defined(CONFIG_IA64_SGI_SN1)
 
-    ht = 0x7F & pciio_config_get(hconn, PCI_CFG_HEADER_TYPE, 1);
+static status __init
+register_with_devfs(void)
+{
+	struct pci_dev * dev;
+	devfs_handle_t device_dir_handle;
+	char devfs_path[40];
 
-    wins = ((ht == 0x00) ? 6 :
-	    (ht == 0x01) ? 2 :
-	    0);
-
-    mwins = iwins = 0;
-
-    hbase = GRAPH_VERTEX_NONE;
-    gbase = GRAPH_VERTEX_NONE;
-
-    for (win = 0; win < wins; win++) {
-
-	base = pciio_config_get(hconn, PCI_CFG_BASE_ADDR(win), 4);
-	if (base & 1) {
-	    space = PCIIO_SPACE_IO;
-	    base &= 0xFFFFFFFC;
-	} else if ((base & 7) == 4) {
-	    space = PCIIO_SPACE_MEM;
-	    base &= 0xFFFFFFF0;
-	    base |= ((pciaddr_t) pciio_config_get(hconn, PCI_CFG_BASE_ADDR(win + 1), 4)) << 32;
-	} else {
-	    space = PCIIO_SPACE_MEM;
-	    base &= 0xFFFFFFF0;
-	}
+	TRACE();
 
-	if (!base)
-	    break;
+	pciba_devfs_handle = devfs_mk_dir(NULL, "pci", NULL);
+	if (pciba_devfs_handle == NULL)
+		return failure;
 
-#if PCIBA_ALIGN_CHECK
-	if (base & (_PAGESZ - 1)) {
-#if DEBUG_PCIBA
-	    PRINT_WARNING("%p pciba: BASE%d not page aligned!\n"
-		    "\tmmap this window at offset 0x%x via \".../pci/%s\"\n",
-		    hconn, win, base,
-		    (space == PCIIO_SPACE_IO) ? "io" : "mem");
-#endif
-	    continue;			/* next window */
-	}
-#endif
+	/* FIXME: don't forget /dev/pci/mem & /dev/pci/io */
+
+	pci_for_each_dev(dev) {
+		sprintf(devfs_path, "%02x/%02x.%x",
+			dev->bus->number,
+			PCI_SLOT(dev->devfn),
+			PCI_FUNC(dev->devfn));
+    
+		device_dir_handle =
+			devfs_mk_dir(pciba_devfs_handle, devfs_path, NULL);
+		if (device_dir_handle == NULL)
+			return failure;
 
-	if ((hbase == GRAPH_VERTEX_NONE) &&
-	    ((GRAPH_SUCCESS !=
-	      hwgraph_path_add(hconn, PCIBA_EDGE_LBL_BASE, &hbase)) ||
-	     (hbase == GRAPH_VERTEX_NONE)))
-	    break;			/* no base vertex, no more windows. */
-
-	if ((gconn != GRAPH_VERTEX_NONE) &&
-	    (gbase == GRAPH_VERTEX_NONE) &&
-	    ((GRAPH_SUCCESS !=
-	      hwgraph_path_add(gconn, PCIBA_EDGE_LBL_BASE, &gbase)) ||
-	     (gbase == GRAPH_VERTEX_NONE)))
-	    break;			/* no base vertex, no more windows. */
-
-	pciba_sub_attach(comm, PCIIO_SPACE_WIN(win), space, base, hbase, gbase, PCIBA_EDGE_LBL_WIN(win));
-
-	if (space == PCIIO_SPACE_IO) {
-	    if (!iwins++) {
-		pciba_sub_attach(comm, PCIIO_SPACE_WIN(win), space, base, hconn, gconn, PCIBA_EDGE_LBL_IO);
-	    }
-	} else {
-	    if (!mwins++) {
-		pciba_sub_attach(comm, PCIIO_SPACE_WIN(win), space, base, hconn, gconn, PCIBA_EDGE_LBL_MEM);
-	    }
+		if (register_pci_device(device_dir_handle, dev) == failure) {
+			devfs_unregister(pciba_devfs_handle);
+			return failure;
+		}
 	}
 
-	if ((base & 7) == 4)
-	    win++;
-    }
-
-    pciba_sub_attach(comm, PCIIO_SPACE_CFG, PCIIO_SPACE_NONE, 0, hconn, gconn, PCIBA_EDGE_LBL_CFG);
-    pciba_sub_attach(comm, PCIBA_SPACE_UDMA, PCIIO_SPACE_NONE, 0, hconn, gconn, PCIBA_EDGE_LBL_DMA);
-#if ULI
-    pciba_sub_attach(comm, PCIIO_SPACE_NONE, PCIIO_SPACE_NONE, 0, hconn, gconn, PCIBA_EDGE_LBL_INTR);
-#endif
+	return success;
+}
 
-    /* XXX should ignore if device is an IOC3 */
-    if (ht == 0x01)
-	base = pciio_config_get(hconn, PCI_EXPANSION_ROM+8, 4);
-    else
-	base = pciio_config_get(hconn, PCI_EXPANSION_ROM, 4);
-
-    base &= 0xFFFFF000;
-
-    if (base) {
-	if (base & (_PAGESZ - 1))
-#if defined(SUPPORT_PRINTING_V_FORMAT)
-	    PRINT_WARNING("%v pciba: ROM is 0x%x\n"
-		    "\tnot page aligned, mmap will be difficult\n",
-		    hconn, base);
 #else
-	    PRINT_WARNING("0x%x pciba: ROM is 0x%x\n"
-		    "\tnot page aligned, mmap will be difficult\n",
-		    hconn, base);
-#endif
-	pciba_sub_attach(comm, PCIIO_SPACE_ROM, PCIIO_SPACE_MEM, base, hconn, gconn, PCIBA_EDGE_LBL_ROM);
-    }
 
-#if !FICUS	/* FICUS shorts the refct by one on path_add */
-    if (hbase != GRAPH_VERTEX_NONE)
-	hwgraph_vertex_unref(hbase);
+extern devfs_handle_t
+devfn_to_vertex(unsigned char busnum, unsigned int devfn);
 
-    if (gbase != GRAPH_VERTEX_NONE)
-	hwgraph_vertex_unref(gbase);
-#endif
+static status __init
+register_with_devfs(void)
+{
+	struct pci_dev * dev;
+	devfs_handle_t device_dir_handle;
 
-    return 0;
-}
+	TRACE();
 
-static void
-pciba_sub_attach2(pciba_comm_t comm,
-		  pciio_space_t space,
-		  pciio_space_t iomem,
-		  pciaddr_t base,
-		  devfs_handle_t from,
-		  char *name,
-		  char *suf,
-		  unsigned bigend)
-{
-    char		nbuf[128];
-    pciba_soft_t            soft;
-    devfs_handle_t	handle = NULL;
-
-    if (suf && *suf) {
-	strcpy(nbuf, name);
-	name = nbuf;
-	strcat(name, suf);
-    }
-
-#if DEBUG_PCIBA
-    printk("pciba_sub_attach2 %p/%s %p at %p[%x]\n",
-	   from, name, space, space_desc, iomem, space_desc, base, from, name);
-#endif
+	/* FIXME: don't forget /dev/.../pci/mem & /dev/.../pci/io */
 
-    if (space < TRACKED_SPACES)
-	if ((soft = comm->soft[space][bigend]) != NULL) {
-	    soft->refct ++;
-	    hwgraph_edge_add(from, soft->vhdl, name);
-	    return;
+	pci_for_each_dev(dev) {
+		device_dir_handle = devfn_to_vertex(dev->bus->number,
+						    dev->devfn);
+		if (device_dir_handle == NULL)
+			return failure;
+	
+		if (register_pci_device(device_dir_handle, dev) == failure) {
+			devfs_unregister(pciba_devfs_handle);
+			return failure;
+		}
 	}
 
-    NEW(soft);
-    if (!soft)
-	return;
-
-    soft->comm = comm;
-    soft->space = space;
-    soft->size = 0;
-    soft->iomem = iomem;
-    soft->base = base;
-    soft->refct = 1;
-
-    if (space == PCIIO_SPACE_NONE)
-	soft->flags = 0;
-    else if (bigend)
-	soft->flags = PCIIO_BYTE_STREAM;
-    else
-	soft->flags = PCIIO_WORD_VALUES;
-
-    handle = hwgraph_register(from, name,
-		0, DEVFS_FL_AUTO_DEVNUM,
-		0, 0,
-		S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, 0, 0,
-		&pciba_fops, NULL);
-    soft->vhdl = handle;
-    pciba_soft_set(soft->vhdl, soft);
-    if (space < TRACKED_SPACES)
-	comm->soft[space][bigend] = soft;
-    comm->refct ++;
+	return success;
 }
 
-static void
-pciba_sub_attach1(pciba_comm_t comm,
-		  pciio_space_t space,
-		  pciio_space_t iomem,
-		  pciaddr_t base,
-		  devfs_handle_t hfrom,
-		  devfs_handle_t gfrom,
-		  char *name,
-		  char *suf,
-		  unsigned bigend)
-{
-    pciba_sub_attach2(comm, space, iomem, base, hfrom, name, suf, bigend);
-    if ((gfrom != GRAPH_VERTEX_NONE) && (gfrom != hfrom))
-	pciba_sub_attach2(comm, space, iomem, base, gfrom, name, suf, bigend);
-}
+#endif /* CONFIG_IA64_SGI_SN1 */
+
+
+static void __exit
+unregister_with_devfs(void)
+{
+	struct list_head * lhp;
+	struct node_data * nd;
+	
+	TRACE();
+
+	list_for_each(lhp, &global_node_list) {
+		nd = list_entry(lhp, struct node_data, global_node_list);
+		devfs_unregister(nd->devfs_handle);
+	}
 
-static void
-pciba_sub_attach(pciba_comm_t comm,
-		 pciio_space_t space,
-		 pciio_space_t iomem,
-		 pciaddr_t base,
-		 devfs_handle_t hfrom,
-		 devfs_handle_t gfrom,
-		 char *name)
-{
-    pciba_sub_attach1(comm, space, iomem, base, hfrom, gfrom, name, NULL, 0);
-    if (iomem != PCIIO_SPACE_NONE) {
-	pciba_sub_attach1(comm, space, iomem, base, hfrom, gfrom, name, "_le", 0);
-	pciba_sub_attach1(comm, space, iomem, base, hfrom, gfrom, name, "_be", 1);
-    }
 }
 
-#ifdef LATER
-static void
-pciba_reload_me(devfs_handle_t pconn_vhdl)
+
+struct node_data * new_node(void)
 {
-    devfs_handle_t            vhdl;
+	struct node_data * node;
+	
+	TRACE();
+	
+	node = kmalloc(sizeof(struct node_data), GFP_KERNEL);
+	if (node == NULL)
+		return NULL;
+	list_add(&node->global_node_list, &global_node_list);
+	return node;
+}
 
-#if DEBUG_PCIBA
-    printf("pciba_reload_me(%v)\n", pconn_vhdl);
-#endif
 
-    if (GRAPH_SUCCESS !=
-	hwgraph_traverse(pconn_vhdl, PCIBA_EDGE_LBL_CFG, &vhdl))
-	return;
+void dma_cleanup(struct node_data * dma_node)
+{
+	TRACE();
 
-    hwgraph_vertex_unref(vhdl);
+	/* FIXME: should free these allocations */
+#ifdef DEBUG_PCIBA
+	dump_allocations(&dma_node->u.dma.dma_allocs);
+#endif
+	devfs_unregister(dma_node->devfs_handle);
 }
-#endif	/* LATER */
 
-static                  pciba_bus_t
-pciba_find_bus(devfs_handle_t pconn, int cflag)
+
+void init_dma_node(struct node_data * node,
+		   struct pci_dev * dev, devfs_handle_t dh)
 {
-    pciio_info_t            pciio_info;
-    devfs_handle_t          master;
-    arbitrary_info_t        ainfo;
-    pciba_bus_t             bus;
+	TRACE();
 
-    pciio_info = pciio_info_get(pconn);
-    master = pciio_info_master_get(pciio_info);
+	node->devfs_handle = dh;
+	node->u.dma.dev = dev;
+	node->cleanup = dma_cleanup;
+	INIT_LIST_HEAD(&node->u.dma.dma_allocs);
+}
 
-    if (GRAPH_SUCCESS ==
-	hwgraph_info_get_LBL(master, PCIBA_INFO_LBL_BUS, &ainfo))
-	return (pciba_bus_t) ainfo;
 
-    if (!cflag)
-	return 0;
+void rom_cleanup(struct node_data * rom_node)
+{
+	TRACE();
 
-    NEW(bus);
-    if (!bus)
-	return 0;
+	if (rom_node->u.rom.mmapped)
+		pci_write_config_dword(rom_node->u.rom.dev,
+				       PCI_ROM_ADDRESS,
+				       rom_node->u.rom.saved_rom_base_reg);
+	devfs_unregister(rom_node->devfs_handle);
+}
 
-    sema_init(&bus->lock, 1);
 
-    ainfo = (arbitrary_info_t) bus;
-    hwgraph_info_add_LBL(master, PCIBA_INFO_LBL_BUS, ainfo);
-    hwgraph_info_get_LBL(master, PCIBA_INFO_LBL_BUS, &ainfo);
-    if ((pciba_bus_t) ainfo != bus)
-	DEL(bus);
-#if DEBUG_PCIBA
-    else
-	printk("pcbia_find_bus: new bus at %p\n", master);
-#endif
+void init_rom_node(struct node_data * node,
+		   struct pci_dev * dev, devfs_handle_t dh)
+{
+	TRACE();
 
-    return (pciba_bus_t) ainfo;
+	node->devfs_handle = dh;
+	node->u.rom.dev = dev;
+	node->cleanup = rom_cleanup;
+	node->u.rom.mmapped = false;
 }
 
-#ifdef LATER
-static void
-pciba_map_push(pciba_bus_t bus, pciba_map_t map)
+
+static status __init
+register_pci_device(devfs_handle_t device_dir_handle, struct pci_dev * dev)
 {
-#if DEBUG_PCIBA
-    printk("pciba_map_push(bus=0x%x, map=0x%x, hdl=0x%x\n",
-	   bus, map, map->handle);
-#endif
-    pciba_bus_lock(bus);
-    map->next = bus->maps;
-    bus->maps = map;
-    pciba_bus_unlock(bus);
-}
-
-static                  pciba_map_t
-pciba_map_pop_hdl(pciba_bus_t bus, __psunsigned_t handle)
-{
-    pciba_map_h             hdl;
-    pciba_map_t             map;
-
-    pciba_bus_lock(bus);
-    for (hdl = &bus->maps; map = *hdl; hdl = &map->next)
-	if (map->handle == handle) {
-	    *hdl = map->next;
-	    break;
+	struct node_data * nd;
+	char devfs_path[20];
+	devfs_handle_t node_devfs_handle;
+	int ri;
+
+	TRACE();
+
+
+	/* register nodes for all the device's base address registers */
+	for (ri = 0; ri < PCI_ROM_RESOURCE; ri++) {
+		if (pci_resource_len(dev, ri) != 0) {
+			sprintf(devfs_path, "base/%d", ri);
+			if (devfs_register(device_dir_handle, devfs_path,
+					   DEVFS_FL_NONE,
+					   0, 0,
+					   S_IFREG | S_IRUSR | S_IWUSR,
+					   &base_fops, 
+					   &dev->resource[ri]) == NULL)
+				return failure;
+		}
 	}
-    pciba_bus_unlock(bus);
-#if DEBUG_PCIBA
-    printk("pciba_map_pop_va(bus=0x%x, handle=0x%x) returns map=0x%x\n",
-	   bus, handle, map);
+	
+	/* register a node corresponding to the first MEM resource on
+           the device */
+	for (ri = 0; ri < PCI_ROM_RESOURCE; ri++) {
+		if (dev->resource[ri].flags & IORESOURCE_MEM &&
+		    pci_resource_len(dev, ri) != 0) {
+			if (devfs_register(device_dir_handle, "mem",
+					   DEVFS_FL_NONE, 0, 0,
+					   S_IFREG | S_IRUSR | S_IWUSR,
+					   &base_fops, 
+					   &dev->resource[ri]) == NULL)
+				return failure;
+			break;
+		}
+	}
+
+	/* also register a node corresponding to the first IO resource
+           on the device */
+	for (ri = 0; ri < PCI_ROM_RESOURCE; ri++) {
+		if (dev->resource[ri].flags & IORESOURCE_IO &&
+		    pci_resource_len(dev, ri) != 0) {
+			if (devfs_register(device_dir_handle, "io",
+					   DEVFS_FL_NONE, 0, 0,
+					   S_IFREG | S_IRUSR | S_IWUSR,
+					   &base_fops, 
+					   &dev->resource[ri]) == NULL)
+				return failure;
+			break;
+		}
+	}
+
+	/* register a node corresponding to the device's ROM resource,
+           if present */
+	if (pci_resource_len(dev, PCI_ROM_RESOURCE) != 0) {
+		nd = new_node();
+		if (nd == NULL)
+			return failure;
+		node_devfs_handle = devfs_register(device_dir_handle, "rom",
+						   DEVFS_FL_NONE, 0, 0,
+						   S_IFREG | S_IRUSR,
+						   &rom_fops, nd);
+		if (node_devfs_handle == NULL)
+			return failure;
+		init_rom_node(nd, dev, node_devfs_handle);
+	}
+
+	/* register a node that allows ioctl's to read and write to
+           the device's config space */
+	if (devfs_register(device_dir_handle, "config", DEVFS_FL_NONE,
+			   0, 0, S_IFREG | S_IRUSR | S_IWUSR,
+			   &config_fops, dev) == NULL)
+		return failure;
+
+
+	/* finally, register a node that allows ioctl's to allocate
+           and free DMA buffers, as well as memory map those
+           buffers. */
+	nd = new_node();
+	if (nd == NULL)
+		return failure;
+	node_devfs_handle =
+		devfs_register(device_dir_handle, "dma", DEVFS_FL_NONE,
+			       0, 0, S_IFREG | S_IRUSR | S_IWUSR,
+			       &dma_fops, nd);
+	if (node_devfs_handle == NULL)
+		return failure;
+	init_dma_node(nd, dev, node_devfs_handle);
+
+#ifdef DEBUG_PCIBA
+	dump_nodes(&global_node_list);
 #endif
-    return map;
+	
+	return success;
 }
 
-/* ================================================================
- *            driver teardown, unregister and unload
- */
-int
-pciba_unload(void)
-{
-#if DEBUG_PCIBA
-    printk("pciba_unload()\n");
-#endif
 
-    if (atomic_read(&pciba_prevent_unload))
-	return -1;
+static int
+generic_open(struct inode * inode, struct file * file)
+{
+	TRACE();
 
-    pciio_iterate("pciba_", pciba_unload_me);
+	/* FIXME: should check that they're not trying to open the ROM
+           writable */
 
-    return 0;
+	return 0; /* success */
 }
 
-int
-pciba_unreg(void)
+
+static int
+rom_mmap(struct file * file, struct vm_area_struct * vma)
 {
+	unsigned long pci_pa;
+	struct node_data * nd;
 
-#if DEBUG_PCIBA
-    printf("pciba_unreg()\n");
-#endif
+	TRACE();
 
-    if (atomic_read(&pciba_prevent_unload))
-	return -1;
+	nd = (struct node_data * )file->private_data;
 
-    pciio_driver_unregister("pciba_");
-    return 0;
+	pci_pa = pci_resource_start(nd->u.rom.dev, PCI_ROM_RESOURCE);
+
+	if (!nd->u.rom.mmapped) {
+		nd->u.rom.mmapped = true;
+		DPRINTF("Enabling ROM address decoder.\n");
+		DPRINTF(
+"rom_mmap: FIXME: some cards do not allow both ROM and memory addresses to\n"
+"rom_mmap: FIXME: be enabled simultaneously, as they share a decoder.\n");
+		pci_read_config_dword(nd->u.rom.dev, PCI_ROM_ADDRESS,
+				      &nd->u.rom.saved_rom_base_reg);
+		DPRINTF("ROM base address contains %x\n",
+			nd->u.rom.saved_rom_base_reg);
+		pci_write_config_dword(nd->u.rom.dev, PCI_ROM_ADDRESS,
+				       nd->u.rom.saved_rom_base_reg |
+				       PCI_ROM_ADDRESS_ENABLE);
+	}
+	
+	return mmap_pci_address(vma, pci_pa);
 }
 
-int
-pciba_detach(devfs_handle_t conn)
+
+static int
+rom_release(struct inode * inode, struct file * file)
 {
-    devfs_handle_t          base;
-    pciba_bus_t             bus;
-    devfs_handle_t          gconn;
-    devfs_handle_t          gbase;
+	struct node_data * nd;
 
-    pciio_info_t            pciio_info;
-    devfs_handle_t          master;
-    arbitrary_info_t        ainfo;
-    int			    ret;
+	TRACE();
 
-#if DEBUG_PCIBA
-    printf("pciba_detach(%v)\n", conn);
-#endif
+	nd = (struct node_data * )file->private_data;
 
-    if ((GRAPH_SUCCESS !=
-	 hwgraph_traverse(conn, ".guest", &gconn)) ||
-	(conn == gconn))
-	gconn = GRAPH_VERTEX_NONE;
-
-    if (gconn != GRAPH_VERTEX_NONE) {
-	pciba_sub_detach(gconn, PCIBA_EDGE_LBL_CFG);
-	pciba_sub_detach(gconn, PCIBA_EDGE_LBL_DMA);
-	pciba_sub_detach(gconn, PCIBA_EDGE_LBL_ROM);
-#if ULI
-	pciba_sub_detach(gconn, PCIBA_EDGE_LBL_INTR);
-#endif
-	if (GRAPH_SUCCESS == hwgraph_edge_remove(conn, PCIBA_EDGE_LBL_BASE, &gbase)) {
-	    pciba_sub_detach(gconn, PCIBA_EDGE_LBL_MEM);
-	    pciba_sub_detach(gconn, PCIBA_EDGE_LBL_IO);
-	    pciba_sub_detach(gbase, "0");
-	    pciba_sub_detach(gbase, "1");
-	    pciba_sub_detach(gbase, "2");
-	    pciba_sub_detach(gbase, "3");
-	    pciba_sub_detach(gbase, "4");
-	    pciba_sub_detach(gbase, "5");
-	    hwgraph_vertex_unref(gbase);
-	    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_destroy(gbase))) {
-#if defined(SUPPORT_PRINTING_V_FORMAT)
-		PRINT_WARNING("pciba: hwgraph_vertex_destroy(%v/base) failed (%d)",
-			conn, ret);
-#else
-		PRINT_WARNING("pciba: hwgraph_vertex_destroy(0x%x/base) failed (%d)",
-			conn, ret);
-#endif
-#if DEBUG_REFCT
-		printk("\tretained refct %d\n", hwgraph_vertex_refct(gbase));
-#endif
-	    }
+	if (nd->u.rom.mmapped) {
+		nd->u.rom.mmapped = false;
+		DPRINTF("Disabling ROM address decoder.\n");
+		pci_write_config_dword(nd->u.rom.dev, PCI_ROM_ADDRESS,
+				       nd->u.rom.saved_rom_base_reg);
 	}
-    }
+	return 0; /* indicate success */
+}
 
-    pciba_sub_detach(conn, PCIBA_EDGE_LBL_CFG);
-    pciba_sub_detach(conn, PCIBA_EDGE_LBL_DMA);
-    pciba_sub_detach(conn, PCIBA_EDGE_LBL_ROM);
-#if ULI
-    pciba_sub_detach(conn, PCIBA_EDGE_LBL_INTR);
-#endif
 
-    if (GRAPH_SUCCESS == hwgraph_edge_remove(conn, PCIBA_EDGE_LBL_BASE, &base)) {
-	pciba_sub_detach(conn, PCIBA_EDGE_LBL_MEM);
-	pciba_sub_detach(conn, PCIBA_EDGE_LBL_IO);
-	pciba_sub_detach(base, "0");
-	pciba_sub_detach(base, "1");
-	pciba_sub_detach(base, "2");
-	pciba_sub_detach(base, "3");
-	pciba_sub_detach(base, "4");
-	pciba_sub_detach(base, "5");
-	hwgraph_vertex_unref(base);
-	if (GRAPH_SUCCESS != (ret = hwgraph_vertex_destroy(base))) {
-#if defined(SUPPORT_PRINTING_V_FORMAT)
-	    PRINT_WARNING(CE_WARN, "pciba: hwgraph_vertex_destroy(%v/base) failed (%d)",
-		    conn, ret);
-#else
-	    PRINT_WARNING(CE_WARN, "pciba: hwgraph_vertex_destroy(0x%x/base) failed (%d)",
-		    conn, ret);
-#endif
-#if DEBUG_REFCT
-	    printk("\tretained refct %d\n", hwgraph_vertex_refct(base));
-#endif
-	}
-    }
+static int
+base_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct resource * resource;
 
-    bus = pciba_find_bus(conn, 0);
-    if (bus && !--(bus->refct)) {
+	TRACE();
 
-	pciio_info = pciio_info_get(conn);
+	resource = (struct resource *)file->private_data;
 
-	master = pciio_info_master_get(pciio_info);
+	return mmap_pci_address(vma, resource->start);
+}
 
-	pciba_sub_detach(master, PCIBA_EDGE_LBL_IO);
-	pciba_sub_detach(master, PCIBA_EDGE_LBL_MEM);
-	pciba_sub_detach(master, PCIBA_EDGE_LBL_CFG);
-	hwgraph_info_remove_LBL(master, PCIBA_INFO_LBL_BUS, &ainfo);
 
-#if DEBUG_PCIBA
-	printf("pcbia_detach: DEL(bus) at %v\n", master);
-#endif
-	DEL(bus);
-    } 
+static int
+config_ioctl(struct inode * inode, struct file * file, 
+	     unsigned int cmd, 
+	     unsigned long arg)
+{
+	struct pci_dev * dev;
 
-    return 0;
-}
+	union cfg_data {
+		uint8_t byte;
+		uint16_t word;
+		uint32_t dword;
+	} read_data, write_data;
 
-static void
-pciba_sub_detach1(devfs_handle_t conn,
-		  char *name,
-		  char *suf)
-{
-    devfs_handle_t         vhdl;
-    pciba_soft_t            soft;
-    pciba_comm_t	    comm;
-    int			    ret;
-    char		nbuf[128];
-
-    if (suf && *suf) {
-	strcpy(nbuf, name);
-	name = nbuf;
-	strcat(name, suf);
-    }
-
-    if ((GRAPH_SUCCESS == hwgraph_edge_remove(conn, name, &vhdl)) &&
-	((soft = pciba_soft_get(vhdl)) != NULL)) {
-#if DEBUG_PCIBA
-#if defined(SUPPORT_PRINTING_V_FORMAT)
-	prink("pciba_sub_detach(%v,%s)\n", conn, name);
-#else
-	prink("pciba_sub_detach(0x%x,%s)\n", conn, name);
-#endif
-#endif
+	int dir, size, offset;
 
-	hwgraph_vertex_unref(soft->vhdl);
-#if DEBUG_REFCT
-	printk("\tadjusted refct %d (soft ref: %d)\n",
-		hwgraph_vertex_refct(vhdl),
-		soft->refct);
-#endif
-	if (!--(soft->refct)) {
-	    comm = soft->comm;
-	    if (!--(comm->refct)) {
-		DEL(comm);
-	    }
-	    pciba_soft_set(vhdl, 0);
-	    DEL(soft);
-
-	    hwgraph_vertex_unref(vhdl);
-	    if (GRAPH_SUCCESS != (ret = hwgraph_vertex_destroy(vhdl))) {
-#if defined(SUPPORT_PRINTING_V_FORMAT)
-		PRINT_WARNING("pciba: hwgraph_vertex_destroy(0x%x/%s) failed (%d)",
-			conn, name, ret);
-#else
-		PRINT_WARNING("pciba: hwgraph_vertex_destroy(%v/%s) failed (%d)",
-			conn, name, ret);
-#endif
-#if DEBUG_REFCT
-		printk("\tretained refct %d\n", hwgraph_vertex_refct(vhdl));
-#endif
-	    }
-	}
-    }
-}
+	TRACE();
 
-static void
-pciba_sub_detach(devfs_handle_t conn,
-		 char *name)
-{
-    pciba_sub_detach1(conn, name, "");
-    pciba_sub_detach1(conn, name, "_le");
-    pciba_sub_detach1(conn, name, "_be");
-}
+	DPRINTF("cmd = %x (DIR = %x, TYPE = %x, NR = %x, SIZE = %x)\n", 
+		cmd, 
+		_IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd), _IOC_SIZE(cmd));
+	DPRINTF("arg = %lx\n", arg);
 
-static void
-pciba_unload_me(devfs_handle_t pconn_vhdl)
-{
-    devfs_handle_t         c_vhdl;
+	dev = (struct pci_dev *)file->private_data;
 
-#if DEBUG_PCIBA
-    printf("pciba_unload_me(%v)\n", pconn_vhdl);
-#endif
+	/* PCIIOCCFG{RD,WR}: read and/or write PCI configuration
+	   space. If both, the read happens first (this becomes a swap
+	   operation, atomic with respect to other updates through
+	   this path).  */
 
-    if (GRAPH_SUCCESS !=
-	hwgraph_traverse(pconn_vhdl, PCIBA_EDGE_LBL_CFG, &c_vhdl))
-	return;
+	dir = _IOC_DIR(cmd);
 
-    hwgraph_vertex_unref(c_vhdl);
-}
+#define do_swap(suffix, type)	 					\
+	do {								\
+		if (dir & _IOC_READ) {					\
+			pci_read_config_##suffix(dev, _IOC_NR(cmd), 	\
+						 &read_data.suffix);	\
+		}							\
+		if (dir & _IOC_WRITE) {					\
+			get_user(write_data.suffix, (type)arg);		\
+			pci_write_config_##suffix(dev, _IOC_NR(cmd), 	\
+						  write_data.suffix);	\
+		}							\
+		if (dir & _IOC_READ) {					\
+			put_user(read_data.suffix, (type)arg);		\
+		}							\
+	} while (0)
 
-/* ================================================================
- *            standard unix entry points
- */
+	size = _IOC_SIZE(cmd);
+	offset = _IOC_NR(cmd);
 
-/*ARGSUSED */
-int
-pciba_open(dev_t *devp, int flag, int otyp, struct cred *crp)
-{
+	DPRINTF("sanity check\n");
+	if (((size > 0) || (size <= 4)) &&
+	    ((offset + size) <= 256) &&
+	    (dir & (_IOC_READ | _IOC_WRITE))) {
 
-#if DEBUG_PCIBA
-    printf("pciba_open(%V)\n", *devp);
-#endif
-    return 0;
+		switch (size)
+		{
+		case 1:
+			do_swap(byte, uint8_t *);
+			break;
+		case 2:
+			do_swap(word, uint16_t *);
+			break;
+		case 4:
+			do_swap(dword, uint32_t *);
+			break;
+		default:
+			DPRINTF("invalid ioctl\n");
+			return -EINVAL;
+		}
+	} else
+		return -EINVAL;
+		
+	return 0;
 }
 
-/*ARGSUSED */
-int
-pciba_close(dev_t dev)
-{
-    devfs_handle_t         vhdl = dev_to_vhdl(dev);
-    pciba_soft_t            soft = pciba_soft_get(vhdl);
-
-#if DEBUG_PCIBA
-    printf("pciba_close(%V)\n", dev);
-#endif
 
-    /* if there is pending DMA for this device, hit the
-     * device over the head with a baseball bat and
-     * release the system memory resources.
-     */
-    if (soft && soft->comm->dmap) {
-	pciba_dma_t             next;
-	pciba_dma_t             dmap;
-
-	pciba_soft_lock(soft);
-	if (dmap = soft->comm->dmap) {
-	    soft->comm->dmap = 0;
-
-	    pciio_reset(soft->comm->conn);
-
-	    do {
-		if (!dmap->kaddr)
-		    break;
-		if (!dmap->paddr)
-		    break;
-		if (dmap->bytes < NBPP)
-		    break;
-		next = dmap->next;
-		kvpfree(dmap->kaddr, dmap->bytes / NBPP);
-		dmap->paddr = 0;
-		dmap->bytes = 0;
-		DEL(dmap);
-	    } while (dmap = next);
+#ifdef DEBUG_PCIBA
+static void
+dump_allocations(struct list_head * dalp)
+{
+	struct dma_allocation * dap;
+	struct list_head * p;
+	
+	printk("{\n");
+	list_for_each(p, dalp) {
+		dap = list_entry(p, struct dma_allocation, 
+				 list);
+		printk("  handle = %lx, va = %p\n",
+		       dap->handle, dap->va);
 	}
-	pciba_soft_unlock(soft);
-    }
-    return 0;
+	printk("}\n");
 }
 
-/* ARGSUSED */
-int
-pciba_read(dev_t dev, cred_t *crp)
+static void
+dump_nodes(struct list_head * nodes)
 {
-#if DEBUG_PCIBA
-    printf("pciba_read(%V)\n", dev);
-#endif
-
-    return EINVAL;
+	struct node_data * ndp;
+	struct list_head * p;
+	
+	printk("{\n");
+	list_for_each(p, nodes) {
+		ndp = list_entry(p, struct node_data, 
+				 global_node_list);
+		printk("  %p\n", (void *)ndp);
+	}
+	printk("}\n");
 }
 
-/* ARGSUSED */
-int
-pciba_write(dev_t dev, cred_t *crp)
+
+#if 0
+#define NEW(ptr) (ptr = kmalloc(sizeof (*(ptr)), GFP_KERNEL))
+
+static void
+test_list(void)
 {
-#if DEBUG_PCIBA
-    printf("pciba_write(%V)\n", dev);
-#endif
+	u64 i;
+	LIST_HEAD(the_list);
 
-    return EINVAL;
+	for (i = 0; i < 5; i++) {
+		struct dma_allocation * new_alloc;
+		NEW(new_alloc);
+		new_alloc->va = (void *)i;
+		new_alloc->handle = 5*i;
+		printk("%d - the_list->next = %lx\n", i, the_list.next);
+		list_add(&new_alloc->list, &the_list);
+	}
+	dump_allocations(&the_list);
 }
-
-/*ARGSUSED */
-int
-pciba_ioctl(dev_t dev, int cmd, void *uarg, int mode, cred_t *crp, int *rvalp)
-{
-    devfs_handle_t          vhdl;
-    pciba_soft_t            soft;
-    pciio_space_t           space;
-    ioctl_arg_buffer_t      arg;
-    int                     psize;
-    int                     err = 0;
-
-#if ULI
-    char		    abi = get_current_abi();
-    pciio_intr_t            intr=0;
-    device_desc_t           desc;
-    cpuid_t                 intrcpu;
-    unsigned                lines;
-    struct uli             *uli = 0;
 #endif
-    unsigned                flags;
-    void                   *kaddr = 0;
-    iopaddr_t               paddr;
-    pciba_dma_h             dmah;
-    pciba_dma_t             dmap = 0;
-    pciio_dmamap_t          dmamap = 0;
-    size_t                  bytes;
-    int                     pages;
-    pciaddr_t               daddr;
-
-#if DEBUG_PCIBA
-    printf("pciba_ioctl(%V,0x%x)\n", dev, cmd);
 #endif
 
-    psize = (cmd >> 16) & IOCPARM_MASK;
-
-#if ULI
-    ASSERT(sizeof(struct uliargs) > 8);	/* prevent CFG access conflict */
-    ASSERT(sizeof(struct uliargs) <= IOCPARM_MASK);
-#endif
 
-    arg.ca = uarg;
+static LIST_HEAD(dma_buffer_list);
 
-    if ((psize > 0) && (cmd & (IOC_OUT | IOC_IN))) {
-	if (psize > sizeof(arg))
-	    err = EINVAL;		/* "bad parameter size */
-	else {
-	    if (cmd & IOC_OUT)
-		bzero(arg.data, psize);
-	    if ((cmd & IOC_IN) &&
-		(copyin(uarg, arg.data, psize) < 0))
-		err = EFAULT;		/* "parameter copyin failed" */
-	}
-    }
-    vhdl = dev_to_vhdl(dev);
-    soft = pciba_soft_get(vhdl);
-    space = soft->space;
-
-    if (err == 0) {
-	err = EINVAL;			/* "invalid ioctl for this vertex" */
-	switch (space) {
-#if ULI
-	case PCIIO_SPACE_NONE:		/* the "intr" vertex */
-	    /* PCIIOCSETULI: set up user interrupts.
-	     */
-	    lines = cmd & 15;
-	    if (ABI_IS_64BIT(abi)) {
-		if (cmd != PCIIOCSETULI(lines)) {
-		    err = EINVAL;		/* "invalid ioctl for this vertex" */
-		    break;
-		}
-	    }
-	    else {
-		struct uliargs uliargs;
-
-		if (cmd != PCIIOCSETULI32(lines)) {
-		    err = EINVAL;		/* "invalid ioctl for this vertex" */
-		    break;
-		}
-		
-		uliargs32_to_uliargs(&arg.uli32, &uliargs);
-		arg.uli = uliargs;
-	    }
-	    desc = device_desc_dup(soft->comm->conn);
-	    device_desc_flags_set(desc, (device_desc_flags_get(desc) |
-					 D_INTR_NOTHREAD));
-	    device_desc_intr_swlevel_set(desc, INTR_SWLEVEL_NOTHREAD_DEFAULT);
-	    device_desc_intr_name_set(desc, "PCIBA");
-	    device_desc_default_set(soft->comm->conn, desc);
-
-	    /* When designating interrupts, the slot number
-	     * is taken from the connection point.
-	     * Bits 0..3 are used to select INTA..INTD; more
-	     * than one bit can be specified. These should
-	     * be constructed using PCIIO_INTR_LINE_[ABCD].
-	     */
-	    intr = pciio_intr_alloc
-		(soft->comm->conn, desc, lines, soft->vhdl);
-	    if (intr == 0) {
-		err = ENOMEM;		/* "insufficient resources" */
-		break;
-	    }
-	    intrcpu = cpuvertex_to_cpuid(pciio_intr_cpu_get(intr));
 
-	    if (err = new_uli(&arg.uli, &uli, intrcpu)) {
-		break;			/* "unable to set up ULI" */
-	    }
-	    atomic_inc(&pciba_prevent_unload);
-
-	    pciio_intr_connect(intr, pciba_intr, uli, (void *) 0);
-
-	    /* NOTE: don't set the teardown function
-	     * until the interrupt is connected.
-	     */
-	    uli->teardownarg1 = (__psint_t) intr;
-	    uli->teardown = pciba_clearuli;
-
-	    arg.uli.id = uli->index;
-
-	    if (!ABI_IS_64BIT(abi)) {
-		struct uliargs32 uliargs32;
-		uliargs_to_uliargs32(&arg.uli, &uliargs32);
-		arg.uli32 = uliargs32;
-	    }
+static int
+dma_ioctl(struct inode * inode, struct file * file, 
+	  unsigned int cmd, 
+	  unsigned long arg)
+{
+	struct node_data * nd;
+	uint64_t argv;
+	int result;
+	struct dma_allocation * dma_alloc;
+	struct list_head * iterp;
 
-	    err = 0;
-	    break;
-#endif
+	TRACE();
 
-	case PCIBA_SPACE_UDMA:		/* the "dma" vertex */
+	DPRINTF("cmd = %x\n", cmd);
+	DPRINTF("arg = %lx\n", arg);
 
-	    switch (cmd) {
+	nd = (struct node_data *)file->private_data;
 
-	    case PCIIOCDMAALLOC:
-		/* PCIIOCDMAALLOC: allocate a chunk of physical
-		 * memory and set it up for DMA. Return the
-		 * PCI address that gets to it.
-		 * NOTE: this allocates memory local to the
-		 * CPU doing the ioctl, not local to the
-		 * device that will be doing the DMA.
-		 */
-
-		if (!_CAP_ABLE(CAP_DEVICE_MGT)) {
-		    err = EPERM;
-		    break;
-		}
-		/* separate the halves of the incoming parameter */
-		flags = arg.ud >> 32;
-		bytes = arg.ud & 0xFFFFFFFF;
-
-#if DEBUG_PCIBA
-		printf("pciba: user wants 0x%x bytes of DMA, flags 0x%x\n",
-		       bytes, flags);
+#ifdef DEBUG_PCIBA
+	DPRINTF("at dma_ioctl entry\n");
+	dump_allocations(&nd->u.dma.dma_allocs);
 #endif
 
-		/* round up the requested size to the next highest page */
-		pages = (bytes + NBPP - 1) / NBPP;
-
-		/* make sure the requested size is something reasonable */
-		if (pages > pci_user_dma_max_pages) {
-#if DEBUG_PCIBA
-		    printf("pciba: request for too much buffer space\n");
+	switch (cmd) {
+	case PCIIOCDMAALLOC:
+		/* PCIIOCDMAALLOC: allocate a chunk of physical memory
+		   and set it up for DMA. Return the PCI address that
+		   gets to it.  */
+		DPRINTF("case PCIIOCDMAALLOC (%lx)\n", PCIIOCDMAALLOC);
+		
+		if ( (result = get_user(argv, (uint64_t *)arg)) )
+			return result;
+		DPRINTF("argv (size of buffer) = %lx\n", argv);
+
+		dma_alloc = (struct dma_allocation *)
+			kmalloc(sizeof(struct dma_allocation), GFP_KERNEL);
+		if (dma_alloc == NULL)
+			return -ENOMEM;
+
+		dma_alloc->size = (size_t)argv;
+		dma_alloc->va = pci_alloc_consistent(nd->u.dma.dev,
+						     dma_alloc->size,
+						     &dma_alloc->handle);
+		DPRINTF("dma_alloc->va = %p, dma_alloc->handle = %lx\n",
+			dma_alloc->va, dma_alloc->handle);
+		if (dma_alloc->va == NULL) {
+			kfree(dma_alloc);
+			return -ENOMEM;
+		}
+
+		list_add(&dma_alloc->list, &nd->u.dma.dma_allocs);
+		if ( (result = put_user((uint64_t)dma_alloc->handle, 
+				      (uint64_t *)arg)) ) {
+			DPRINTF("put_user failed\n");
+			pci_free_consistent(nd->u.dma.dev, (size_t)argv,
+					    dma_alloc->va, dma_alloc->handle);
+			kfree(dma_alloc);
+			return result;
+		}
+
+#ifdef DEBUG_PCIBA
+		DPRINTF("after insertion\n");
+		dump_allocations(&nd->u.dma.dma_allocs);
 #endif
-		    err = EINVAL;
-		    break;		/* "request for too much buffer space" */
-		}
-		/* "correct" number of bytes */
-		bytes = pages * NBPP;
+		break;
 
-		/* allocate the space */
-		/* XXX- force to same node as the device? */
-		/* XXX- someday, we want to handle user buffers,
-		 *    and noncontiguous pages, but this will
-		 *      require either fancy mapping or handing
-		 *      a list of blocks back to the user. For
-		 *      now, just tell users to allocate a lot of
-		 *      individual single-pages and manage their
-		 *      scatter-gather manually.
-		 */
-		kaddr = kvpalloc(pages, VM_DIRECT | KM_NOSLEEP, 0);
-		if (kaddr == 0) {
-#if DEBUG_PCIBA
-		    printf("pciba: unable to get %d contiguous pages\n", pages);
-#endif
-		    err = EAGAIN;	/* "insufficient resources, try again later" */
-		    break;
-		}
-#if DEBUG_PCIBA
-		printf("pciba: kaddr is 0x%x\n", kaddr);
-#endif
-		paddr = kvtophys(kaddr);
+	case PCIIOCDMAFREE:
+		DPRINTF("case PCIIOCDMAFREE (%lx)\n", PCIIOCDMAFREE);
 
-		daddr = pciio_dmatrans_addr
-		    (soft->comm->conn, 0, paddr, bytes, flags);
-		if (daddr == 0) {	/* "no direct path available" */
-#if DEBUG_PCIBA
-		    printf("pciba: dmatrans failed, trying dmamap\n");
-#endif
-		    dmamap = pciio_dmamap_alloc
-			(soft->comm->conn, 0, bytes, flags);
-		    if (dmamap == 0) {
-#if DEBUG_PCIBA
-			printf("pciba: unable to allocate dmamap\n");
-#endif
-			err = ENOMEM;
-			break;		/* "out of mapping resources" */
-		    }
-		    daddr = pciio_dmamap_addr
-			(dmamap, paddr, bytes);
-		    if (daddr == 0) {
-#if DEBUG_PCIBA
-			printf("pciba: dmamap_addr failed\n");
-#endif
-			err = EINVAL;
-			break;		/* "can't get there from here" */
-		    }
-		}
-#if DEBUG_PCIBA
-		printf("pciba: daddr is 0x%x\n", daddr);
+		if ( (result = get_user(argv, (uint64_t *)arg)) ) {
+			DPRINTF("get_user failed\n");
+			return result;
+		}
+
+		DPRINTF("argv (physical address of DMA buffer) = %lx\n", argv);
+		list_for_each(iterp, &nd->u.dma.dma_allocs) {
+			struct dma_allocation * da =
+				list_entry(iterp, struct dma_allocation, list);
+			if (da->handle == argv) {
+				pci_free_consistent(nd->u.dma.dev, da->size,
+						    da->va, da->handle);
+				list_del(&da->list);
+				kfree(da);
+#ifdef DEBUG_PCIBA
+				DPRINTF("after deletion\n");
+				dump_allocations(&nd->u.dma.dma_allocs);
 #endif
-		NEW(dmap);
-		if (!dmap) {
-		    err = ENOMEM;
-		    break;		/* "no memory available" */
+				return 0; /* success */
+			}
 		}
-		dmap->bytes = bytes;
-		dmap->pages = pages;
-		dmap->paddr = paddr;
-		dmap->kaddr = kaddr;
-		dmap->map = dmamap;
-		dmap->daddr = daddr;
-		dmap->handle = 0;
-
-#if DEBUG_PCIBA
-		printf("pciba: dmap 0x%x contains va 0x%x bytes 0x%x pa 0x%x pages 0x%x daddr 0x%x\n",
-		       dmap, kaddr, bytes, paddr, pages, daddr);
-#endif
+		/* previously allocated dma buffer wasn't found */
+		DPRINTF("attempt to free invalid dma handle\n");
+		return -EINVAL;
 
-		arg.ud = dmap->daddr;
+	default:
+		DPRINTF("undefined ioctl\n");
+		return -EINVAL;
+	}
 
-		err = 0;
-		break;
+	DPRINTF("success\n");
+	return 0;
+}
+		
 
-	    case PCIIOCDMAFREE:
-		/* PCIIOCDMAFREE: Find the chunk of
-		 * User DMA memory, and release its
-		 * resources back to the system.
-		 */
-
-		if (!_CAP_ABLE(CAP_DEVICE_MGT)) {
-		    err = EPERM;	/* "you can't do that" */
-		    break;
-		}
-		if (soft->comm->dmap == NULL) {
-		    err = EINVAL;	/* "no User DMA to free" */
-		    break;
-		}
-		/* find the request. */
-		daddr = arg.ud;
-		err = EINVAL;		/* "block not found" */
-		pciba_soft_lock(soft);
-		for (dmah = &soft->comm->dmap; dmap = *dmah; dmah = &dmap->next) {
-		    if (dmap->daddr == daddr) {
-			if (dmap->handle != 0) {
-			    dmap = 0;	/* don't DEL this dmap! */
-			    err = EINVAL;	/* "please unmap first" */
-			    break;		/* break outa for loop. */
-			}
-			*dmah = dmap->next;
+static int
+dma_mmap(struct file * file, struct vm_area_struct * vma)
+{
+	struct node_data * nd;
+	struct list_head * iterp;
+	int result;
+	
+	TRACE();
 
-			if (dmamap = dmap->map) {
-			    pciio_dmamap_free(dmamap);
-			    dmamap = 0;	/* don't free it twice! */
+	nd = (struct node_data *)file->private_data;
+	
+	DPRINTF("vma->vm_start is %lx\n", vma->vm_start);
+	DPRINTF("vma->vm_end is %lx\n", vma->vm_end);
+	DPRINTF("offset = %lx\n", vma->vm_pgoff);
+
+	/* get kernel virtual address for the dma buffer (necessary
+	 * for the mmap). */
+	list_for_each(iterp, &nd->u.dma.dma_allocs) {
+		struct dma_allocation * da =
+			list_entry(iterp, struct dma_allocation, list);
+		/* why does mmap shift its offset argument? */
+		if (da->handle == vma->vm_pgoff << PAGE_SHIFT) {
+			DPRINTF("found dma handle\n");
+			if ( (result = mmap_kernel_address(vma,
+							   da->va)) ) {
+				return result; /* failure */
+			} else {
+				/* it seems like at least one of these
+				   should show up in user land....
+				   I'm missing something */
+				*(char *)da->va = 0xaa;
+				strncpy(da->va, "        Toastie!", da->size);
+				if (put_user(0x18badbeeful,
+					     (u64 *)vma->vm_start))
+					DPRINTF("put_user failed?!\n");
+				return 0; /* success */
 			}
-			kvpfree(dmap->kaddr, dmap->bytes / NBPP);
-			DEL(dmap);
-			dmap = 0;	/* don't link this back into the list! */
-			err = 0;	/* "all done" */
-			break;		/* break outa for loop. */
-		    }
-		}
-		pciba_soft_unlock(soft);
-		break;		/* break outa case PCIIOCDMAFREE: */
-	    }
-	    break;		/* break outa case PCIBA_SPACE_UDMA: */
-
-	case PCIIO_SPACE_CFG:
-
-	    /* PCIIOCCFG{RD,WR}: read and/or write
-	     * PCI configuration space. If both,
-	     * the read happens first (this becomes
-	     * a swap operation, atomic with respect
-	     * to other updates through this path).
-	     *
-	     * Should be *last* IOCTl command checked,
-	     * so other patterns can nip useless codes
-	     * out of the space this decodes.
-	     */
-	    err = EINVAL;
-	    if ((psize > 0) || (psize <= 8) &&
-		(((cmd & 0xFF) + psize) <= 256) &&
-		(cmd & (IOC_IN | IOC_OUT))) {
-
-		uint64_t                rdata;
-		uint64_t                wdata;
-		int                     shft;
-
-		shft = 64 - (8 * psize);
-
-		wdata = arg.ud >> shft;
-
-		pciba_soft_lock(soft);
-
-		if (cmd & IOC_OUT)
-		    rdata = pciio_config_get(soft->comm->conn, cmd & 0xFFFF, psize);
-		if (cmd & IOC_IN)
-		    pciio_config_set(soft->comm->conn, cmd & 0xFFFF, psize, wdata);
-
-		pciba_soft_unlock(soft);
 
-		arg.ud = rdata << shft;
-		err = 0;
-		break;
-	    }
-	    break;
-	}
-    }
-    /* done: come here if all went OK.
-     */
-    if ((err == 0) &&
-	((cmd & IOC_OUT) && (psize > 0)) &&
-	copyout(arg.data, uarg, psize))
-	err = EFAULT;
-
-    /* This gets delayed until after the copyout so we
-     * do not free the dmap on a copyout error, or
-     * alternately end up with a dangling allocated
-     * buffer that the user never got back.
-     */
-    if ((err == 0) && dmap) {
-	pciba_soft_lock(soft);
-	dmap->next = soft->comm->dmap;
-	soft->comm->dmap = dmap;
-	pciba_soft_unlock(soft);
-    }
-    if (err) {
-	/* Things went badly. Clean up.
-	 */
-#if ULI
-	if (intr) {
-	    pciio_intr_disconnect(intr);
-	    pciio_intr_free(intr);
-	}
-	if (uli)
-	    free_uli(uli);
-#endif
-	if (dmap) {
-	    if (dmap->map && (dmap->map != dmamap))
-		pciio_dmamap_free(dmap->map);
-	    DEL(dmap);
+		}
 	}
-	if (dmamap)
-	    pciio_dmamap_free(dmamap);
-	if (kaddr)
-	    kvpfree(kaddr, pages);
-    }
-    return *rvalp = err;
+	DPRINTF("attempt to mmap an invalid dma handle\n");
+	return -EINVAL;
 }
 
-/* ================================================================
- *            mapping support
- */
-
-/*ARGSUSED */
-int
-pciba_map(dev_t dev, vhandl_t *vt,
-	  off_t off, size_t len, uint32_t prot)
-{
-    devfs_handle_t          vhdl = dev_to_vhdl(dev);
-    pciba_soft_t            soft = pciba_soft_get(vhdl);
-    devfs_handle_t          conn = soft->comm->conn;
-    pciio_space_t           space = soft->space;
-    size_t                  pages = (len + NBPP - 1) / NBPP;
-    pciio_piomap_t          pciio_piomap = 0;
-    caddr_t                 kaddr;
-    pciba_map_t             map;
-    pciba_dma_t             dmap;
 
-#if DEBUG_PCIBA
-    printf("pciba_map(%V,vt=0x%x)\n", dev, vt);
-#endif
+static int
+mmap_pci_address(struct vm_area_struct * vma, unsigned long pci_va)
+{
+	unsigned long pci_pa;
 
-    if (space == PCIBA_SPACE_UDMA) {
-	pciba_soft_lock(soft);
+	TRACE();
 
-	for (dmap = soft->comm->dmap; dmap != NULL; dmap = dmap->next) {
-	    if (off == dmap->daddr) {
-		if (pages != dmap->pages) {
-		    pciba_soft_unlock(soft);
-		    return EINVAL;	/* "size mismatch" */
-		}
-		v_mapphys(vt, dmap->kaddr, dmap->bytes);
-		dmap->handle = v_gethandle(vt);
-		pciba_soft_unlock(soft);
-#if DEBUG_PCIBA
-		printf("pciba: mapped dma at kaddr 0x%x via handle 0x%x\n",
-		       dmap->kaddr, dmap->handle);
-#endif
-		return 0;
-	    }
-	}
-	pciba_soft_unlock(soft);
-	return EINVAL;			/* "block not found" */
-    }
-    if (soft->iomem == PCIIO_SPACE_NONE)
-	return EINVAL;			/* "mmap not supported" */
-
-    kaddr = (caddr_t) pciio_pio_addr
-	(conn, 0, space, off, len, &pciio_piomap, soft->flags | PCIIO_FIXED );
-
-#if DEBUG_PCIBA
-    printf("pciba: mapped %R[0x%x..0x%x] via map 0x%x to kaddr 0x%x\n",
-	   space, space_desc, off, off + len - 1, pciio_piomap, kaddr);
-#endif
+	DPRINTF("vma->vm_start is %lx\n", vma->vm_start);
+	DPRINTF("vma->vm_end is %lx\n", vma->vm_end);
 
-    if (kaddr == NULL)
-	return EINVAL;			/* "you can't get there from here" */
+	/* the size of the vma doesn't necessarily correspond to the
+           size specified in the mmap call.  So we can't really do any
+           kind of sanity check here.  This is a dangerous driver, and
+           it's very easy for a user process to kill the machine.  */
 
-    NEW(map);
-    if (map == NULL) {
-	if (pciio_piomap)
-	    pciio_piomap_free(pciio_piomap);
-	return ENOMEM;			/* "unable to get memory resources */
-    }
-#ifdef LATER
-    map->uthread = curuthread;
-#endif
-    map->handle = v_gethandle(vt);
-    map->uvaddr = v_getaddr(vt);
-    map->map = pciio_piomap;
-    map->space = soft->iomem;
-    map->base = soft->base + off;
-    map->size = len;
-    pciba_map_push(soft->comm->bus, map);
-
-    /* Inform the system of the correct
-     * kvaddr corresponding to the thing
-     * that is being mapped.
-     */
-    v_mapphys(vt, kaddr, len);
-
-    return 0;
-}
-
-/*ARGSUSED */
-int
-pciba_unmap(dev_t dev, vhandl_t *vt)
-{
-    devfs_handle_t         vhdl = dev_to_vhdl(dev);
-    pciba_soft_t            soft = pciba_soft_get(vhdl);
-    pciba_bus_t             bus = soft->comm->bus;
-    pciba_map_t             map;
-    __psunsigned_t          handle = v_gethandle(vt);
+	DPRINTF("PCI base at virtual address %lx\n", pci_va);
+	/* the __pa macro is intended for region 7 on IA64, so it
+	   doesn't work for region 6 */
+  	/* pci_pa = __pa(pci_va); */
+	/* should be replaced by __tpa or equivalent (preferably a
+	   generic equivalent) */
+	pci_pa = pci_va & ~0xe000000000000000ul;
+	DPRINTF("PCI base at physical address %lx\n", pci_pa);
 
-#if DEBUG_PCIBA
-    printf("pciba_unmap(%V,vt=%x)\n", dev, vt);
-#endif
+	/* there are various arch-specific versions of this function
+           defined in linux/drivers/char/mem.c, but it would be nice
+           if all architectures put it in pgtable.h.  it's defined
+           there for ia64.... */
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
 
-    /* If this is a userDMA buffer,
-     * make a note that it has been unmapped
-     * so it can be released.
-     */
-    if (soft->comm->dmap) {
-	pciba_dma_t             dmap;
-
-	pciba_soft_lock(soft);
-	for (dmap = soft->comm->dmap; dmap != NULL; dmap = dmap->next)
-	    if (handle == dmap->handle) {
-		dmap->handle = 0;
-		pciba_soft_unlock(soft);
-#if DEBUG_PCIBA
-		printf("pciba: unmapped dma at kaddr 0x%x via handle 0x%x\n",
-		       dmap->kaddr, handle);
-#endif
-		return 0;		/* found userPCI */
-	    }
-	pciba_soft_unlock(soft);
-    }
-    map = pciba_map_pop_hdl(bus, handle);
-    if (map == NULL)
-	return EINVAL;			/* no match */
+	vma->vm_flags |= VM_NONCACHED | VM_RESERVED | VM_IO;
 
-    if (map->map)
-	pciio_piomap_free(map->map);
-    DEL(map);
-
-    return (0);				/* all done OK */
+	return io_remap_page_range(vma->vm_start, pci_pa, 
+				   vma->vm_end-vma->vm_start,
+				   vma->vm_page_prot);
 }
 
-#if ULI
-void
-pciba_clearuli(struct uli *uli)
+
+static int
+mmap_kernel_address(struct vm_area_struct * vma, void * kernel_va)
 {
-    pciio_intr_t            intr = (pciio_intr_t) uli->teardownarg1;
+	unsigned long kernel_pa;
 
-#if DEBUG_PCIBA
-    printf("pciba_clearuli(0x%x)\n", uli);
-#endif
+	TRACE();
 
-    pciio_intr_disconnect(intr);
-    pciio_intr_free(intr);
-    atomic_dec(&pciba_prevent_unload);
-}
+	DPRINTF("vma->vm_start is %lx\n", vma->vm_start);
+	DPRINTF("vma->vm_end is %lx\n", vma->vm_end);
 
-void
-pciba_intr(intr_arg_t arg)
-{
-    struct uli             *uli = (struct uli *) arg;
-    int                     ulinum = uli->index;
+	/* the size of the vma doesn't necessarily correspond to the
+           size specified in the mmap call.  So we can't really do any
+           kind of sanity check here.  This is a dangerous driver, and
+           it's very easy for a user process to kill the machine.  */
 
-    extern void frs_handle_uli(void);
+	DPRINTF("mapping virtual address %p\n", kernel_va);
+	kernel_pa = __pa(kernel_va);
+	DPRINTF("mapping physical address %lx\n", kernel_pa);
 
-    if (ulinum >= 0 && ulinum < MAX_ULIS) {
-	    uli_callup(ulinum);
+	vma->vm_flags |= VM_NONCACHED | VM_RESERVED | VM_IO;
 
-	    if (private.p_frs_flags)
-		    frs_handle_uli();
-    }
-}
-#endif
-#endif /* LATER - undef as we implement each routine */
+	return remap_page_range(vma->vm_start, kernel_pa, 
+				vma->vm_end-vma->vm_start,
+				vma->vm_page_prot);
+}	

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)