aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-12-22 16:53:25 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-22 16:53:25 -0800
commit2e28d87623f3bd75960d477473e40ee37ad26942 (patch)
tree30372bdeb7b354a26f1cac12fbf11aaea663a4bc
parent35ffac9ed3ea7a217e5fd11007e8ba0aa0549b01 (diff)
downloadpatches-2e28d87623f3bd75960d477473e40ee37ad26942.tar.gz
staging patches added
-rw-r--r--series4
-rw-r--r--staging/staging-altpciechdma-remove-driver.patch1263
-rw-r--r--staging/staging-p9auth-remove-driver-from-tree.patch467
-rw-r--r--staging/staging-remove-the-b3dfg-driver.patch1172
4 files changed, 2906 insertions, 0 deletions
diff --git a/series b/series
index d53a39aed4933c..17a67d8293950c 100644
--- a/series
+++ b/series
@@ -260,3 +260,7 @@ staging/staging-wlan-ng-initialise-mibitem.patch
staging/staging-dt3155-coding-style-cleanups-for-allocator-code.patch
staging/staging-dt3155-coding-style-cleanups-for-the-.h-files.patch
+staging/staging-remove-the-b3dfg-driver.patch
+staging/staging-p9auth-remove-driver-from-tree.patch
+staging/staging-altpciechdma-remove-driver.patch
+
diff --git a/staging/staging-altpciechdma-remove-driver.patch b/staging/staging-altpciechdma-remove-driver.patch
new file mode 100644
index 00000000000000..d477489de6cbc5
--- /dev/null
+++ b/staging/staging-altpciechdma-remove-driver.patch
@@ -0,0 +1,1263 @@
+From foo@baz Tue Dec 22 16:38:10 PST 2009
+Date: Tue, 22 Dec 2009 16:38:10 -0800
+To: Greg KH <greg@kroah.com>
+From: Greg Kroah-Hartman <gregkh@suse.de>
+Subject: Staging: altpciechdma: remove driver
+
+No one seems to be able to maintain this, or merge it into mainline, so
+remove it.
+
+Cc: Leon Woestenberg <leon@sidebranch.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/staging/Kconfig | 2
+ drivers/staging/Makefile | 1
+ drivers/staging/altpciechdma/Kconfig | 10
+ drivers/staging/altpciechdma/Makefile | 2
+ drivers/staging/altpciechdma/TODO | 15
+ drivers/staging/altpciechdma/altpciechdma.c | 1182 ----------------------------
+ 6 files changed, 1212 deletions(-)
+
+--- a/drivers/staging/altpciechdma/altpciechdma.c
++++ /dev/null
+@@ -1,1182 +0,0 @@
+-/**
+- * Driver for Altera PCIe core chaining DMA reference design.
+- *
+- * Copyright (C) 2008 Leon Woestenberg <leon.woestenberg@axon.tv>
+- * Copyright (C) 2008 Nickolas Heppermann <heppermannwdt@gmail.com>
+- *
+- * 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.,
+- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+- *
+- *
+- * Rationale: This driver exercises the chaining DMA read and write engine
+- * in the reference design. It is meant as a complementary reference
+- * driver that can be used for testing early designs as well as a basis to
+- * write your custom driver.
+- *
+- * Status: Test results from Leon Woestenberg <leon.woestenberg@axon.tv>:
+- *
+- * Sendero Board w/ Cyclone II EP2C35F672C6N, PX1011A PCIe x1 PHY on a
+- * Dell Precision 370 PC, x86, kernel 2.6.20 from Ubuntu 7.04.
+- *
+- * Sendero Board w/ Cyclone II EP2C35F672C6N, PX1011A PCIe x1 PHY on a
+- * Freescale MPC8313E-RDB board, PowerPC, 2.6.24 w/ Freescale patches.
+- *
+- * Driver tests passed with PCIe Compiler 8.1. With PCIe 8.0 the DMA
+- * loopback test had reproducable compare errors. I assume a change
+- * in the compiler or reference design, but could not find evidence nor
+- * documentation on a change or fix in that direction.
+- *
+- * The reference design does not have readable locations and thus a
+- * dummy read, used to flush PCI posted writes, cannot be performed.
+- *
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/cdev.h>
+-#include <linux/delay.h>
+-#include <linux/dma-mapping.h>
+-#include <linux/init.h>
+-#include <linux/interrupt.h>
+-#include <linux/io.h>
+-#include <linux/jiffies.h>
+-#include <linux/module.h>
+-#include <linux/pci.h>
+-
+-
+-/* by default do not build the character device interface */
+-/* XXX It is non-functional yet */
+-#ifndef ALTPCIECHDMA_CDEV
+-# define ALTPCIECHDMA_CDEV 0
+-#endif
+-
+-/* build the character device interface? */
+-#if ALTPCIECHDMA_CDEV
+-# define MAX_CHDMA_SIZE (8 * 1024 * 1024)
+-# include "mapper_user_to_sg.h"
+-#endif
+-
+-/** driver name, mimicks Altera naming of the reference design */
+-#define DRV_NAME "altpciechdma"
+-/** number of BARs on the device */
+-#define APE_BAR_NUM (6)
+-/** BAR number where the RCSLAVE memory sits */
+-#define APE_BAR_RCSLAVE (0)
+-/** BAR number where the Descriptor Header sits */
+-#define APE_BAR_HEADER (2)
+-
+-/** maximum size in bytes of the descriptor table, chdma logic limit */
+-#define APE_CHDMA_TABLE_SIZE (4096)
+-/* single transfer must not exceed 255 table entries. worst case this can be
+- * achieved by 255 scattered pages, with only a single byte in the head and
+- * tail pages. 253 * PAGE_SIZE is a safe upper bound for the transfer size.
+- */
+-#define APE_CHDMA_MAX_TRANSFER_LEN (253 * PAGE_SIZE)
+-
+-/**
+- * Specifies those BARs to be mapped and the length of each mapping.
+- *
+- * Zero (0) means do not map, otherwise specifies the BAR lengths to be mapped.
+- * If the actual BAR length is less, this is considered an error; then
+- * reconfigure your PCIe core.
+- *
+- * @see ug_pci_express 8.0, table 7-2 at page 7-13.
+- */
+-static const unsigned long bar_min_len[APE_BAR_NUM] =
+- { 32768, 0, 256, 0, 32768, 0 };
+-
+-/**
+- * Descriptor Header, controls the DMA read engine or write engine.
+- *
+- * The descriptor header is the main data structure for starting DMA transfers.
+- *
+- * It sits in End Point (FPGA) memory BAR[2] for 32-bit or BAR[3:2] for 64-bit.
+- * It references a descriptor table which exists in Root Complex (PC) memory.
+- * Writing the rclast field starts the DMA operation, thus all other structures
+- * and fields must be setup before doing so.
+- *
+- * @see ug_pci_express 8.0, tables 7-3, 7-4 and 7-5 at page 7-14.
+- * @note This header must be written in four 32-bit (PCI DWORD) writes.
+- */
+-struct ape_chdma_header {
+- /**
+- * w0 consists of two 16-bit fields:
+- * lsb u16 number; number of descriptors in ape_chdma_table
+- * msb u16 control; global control flags
+- */
+- u32 w0;
+- /* bus address to ape_chdma_table in Root Complex memory */
+- u32 bdt_addr_h;
+- u32 bdt_addr_l;
+- /**
+- * w3 consists of two 16-bit fields:
+- * - lsb u16 rclast; last descriptor number available in Root Complex
+- * - zero (0) means the first descriptor is ready,
+- * - one (1) means two descriptors are ready, etc.
+- * - msb u16 reserved;
+- *
+- * @note writing to this memory location starts the DMA operation!
+- */
+- u32 w3;
+-} __attribute__ ((packed));
+-
+-/**
+- * Descriptor Entry, describing a (non-scattered) single memory block transfer.
+- *
+- * There is one descriptor for each memory block involved in the transfer, a
+- * block being a contiguous address range on the bus.
+- *
+- * Multiple descriptors are chained by means of the ape_chdma_table data
+- * structure.
+- *
+- * @see ug_pci_express 8.0, tables 7-6, 7-7 and 7-8 at page 7-14 and page 7-15.
+- */
+-struct ape_chdma_desc {
+- /**
+- * w0 consists of two 16-bit fields:
+- * number of DWORDS to transfer
+- * - lsb u16 length;
+- * global control
+- * - msb u16 control;
+- */
+- u32 w0;
+- /* address of memory in the End Point */
+- u32 ep_addr;
+- /* bus address of source or destination memory in the Root Complex */
+- u32 rc_addr_h;
+- u32 rc_addr_l;
+-} __attribute__ ((packed));
+-
+-/**
+- * Descriptor Table, an array of descriptors describing a chained transfer.
+- *
+- * An array of descriptors, preceded by workspace for the End Point.
+- * It exists in Root Complex memory.
+- *
+- * The End Point can update its last completed descriptor number in the
+- * eplast field if requested by setting the EPLAST_ENA bit either
+- * globally in the header's or locally in any descriptor's control field.
+- *
+- * @note this structure may not exceed 4096 bytes. This results in a
+- * maximum of 4096 / (4 * 4) - 1 = 255 descriptors per chained transfer.
+- *
+- * @see ug_pci_express 8.0, tables 7-9, 7-10 and 7-11 at page 7-17 and page 7-18.
+- */
+-struct ape_chdma_table {
+- /* workspace 0x00-0x0b, reserved */
+- u32 reserved1[3];
+- /* workspace 0x0c-0x0f, last descriptor handled by End Point */
+- u32 w3;
+- /* the actual array of descriptors
+- * 0x10-0x1f, 0x20-0x2f, ... 0xff0-0xfff (255 entries)
+- */
+- struct ape_chdma_desc desc[255];
+-} __attribute__ ((packed));
+-
+-/**
+- * Altera PCI Express ('ape') board specific book keeping data
+- *
+- * Keeps state of the PCIe core and the Chaining DMA controller
+- * application.
+- */
+-struct ape_dev {
+- /** the kernel pci device data structure provided by probe() */
+- struct pci_dev *pci_dev;
+- /**
+- * kernel virtual address of the mapped BAR memory and IO regions of
+- * the End Point. Used by map_bars()/unmap_bars().
+- */
+- void * __iomem bar[APE_BAR_NUM];
+- /** kernel virtual address for Descriptor Table in Root Complex memory */
+- struct ape_chdma_table *table_virt;
+- /**
+- * bus address for the Descriptor Table in Root Complex memory, in
+- * CPU-native endianess
+- */
+- dma_addr_t table_bus;
+- /* if the device regions could not be allocated, assume and remember it
+- * is in use by another driver; this driver must not disable the device.
+- */
+- int in_use;
+- /* whether this driver enabled msi for the device */
+- int msi_enabled;
+- /* whether this driver could obtain the regions */
+- int got_regions;
+- /* irq line successfully requested by this driver, -1 otherwise */
+- int irq_line;
+- /* board revision */
+- u8 revision;
+- /* interrupt count, incremented by the interrupt handler */
+- int irq_count;
+-#if ALTPCIECHDMA_CDEV
+- /* character device */
+- dev_t cdevno;
+- struct cdev cdev;
+- /* user space scatter gather mapper */
+- struct sg_mapping_t *sgm;
+-#endif
+-};
+-
+-/**
+- * Using the subsystem vendor id and subsystem id, it is possible to
+- * distinguish between different cards bases around the same
+- * (third-party) logic core.
+- *
+- * Default Altera vendor and device ID's, and some (non-reserved)
+- * ID's are now used here that are used amongst the testers/developers.
+- */
+-static const struct pci_device_id ids[] = {
+- { PCI_DEVICE(0x1172, 0xE001), },
+- { PCI_DEVICE(0x2071, 0x2071), },
+- { 0, }
+-};
+-MODULE_DEVICE_TABLE(pci, ids);
+-
+-#if ALTPCIECHDMA_CDEV
+-/* prototypes for character device */
+-static int sg_init(struct ape_dev *ape);
+-static void sg_exit(struct ape_dev *ape);
+-#endif
+-
+-/**
+- * altpciechdma_isr() - Interrupt handler
+- *
+- */
+-static irqreturn_t altpciechdma_isr(int irq, void *dev_id)
+-{
+- struct ape_dev *ape = (struct ape_dev *)dev_id;
+- if (!ape)
+- return IRQ_NONE;
+- ape->irq_count++;
+- return IRQ_HANDLED;
+-}
+-
+-static int __devinit scan_bars(struct ape_dev *ape, struct pci_dev *dev)
+-{
+- int i;
+- for (i = 0; i < APE_BAR_NUM; i++) {
+- unsigned long bar_start = pci_resource_start(dev, i);
+- if (bar_start) {
+- unsigned long bar_end = pci_resource_end(dev, i);
+- unsigned long bar_flags = pci_resource_flags(dev, i);
+- printk(KERN_DEBUG "BAR%d 0x%08lx-0x%08lx flags 0x%08lx\n",
+- i, bar_start, bar_end, bar_flags);
+- }
+- }
+- return 0;
+-}
+-
+-/**
+- * Unmap the BAR regions that had been mapped earlier using map_bars()
+- */
+-static void unmap_bars(struct ape_dev *ape, struct pci_dev *dev)
+-{
+- int i;
+- for (i = 0; i < APE_BAR_NUM; i++) {
+- /* is this BAR mapped? */
+- if (ape->bar[i]) {
+- /* unmap BAR */
+- pci_iounmap(dev, ape->bar[i]);
+- ape->bar[i] = NULL;
+- }
+- }
+-}
+-
+-/**
+- * Map the device memory regions into kernel virtual address space after
+- * verifying their sizes respect the minimum sizes needed, given by the
+- * bar_min_len[] array.
+- */
+-static int __devinit map_bars(struct ape_dev *ape, struct pci_dev *dev)
+-{
+- int rc;
+- int i;
+- /* iterate through all the BARs */
+- for (i = 0; i < APE_BAR_NUM; i++) {
+- unsigned long bar_start = pci_resource_start(dev, i);
+- unsigned long bar_end = pci_resource_end(dev, i);
+- unsigned long bar_length = bar_end - bar_start + 1;
+- ape->bar[i] = NULL;
+- /* do not map, and skip, BARs with length 0 */
+- if (!bar_min_len[i])
+- continue;
+- /* do not map BARs with address 0 */
+- if (!bar_start || !bar_end) {
+- printk(KERN_DEBUG "BAR #%d is not present?!\n", i);
+- rc = -1;
+- goto fail;
+- }
+- bar_length = bar_end - bar_start + 1;
+- /* BAR length is less than driver requires? */
+- if (bar_length < bar_min_len[i]) {
+- printk(KERN_DEBUG "BAR #%d length = %lu bytes but driver "
+- "requires at least %lu bytes\n",
+- i, bar_length, bar_min_len[i]);
+- rc = -1;
+- goto fail;
+- }
+- /* map the device memory or IO region into kernel virtual
+- * address space */
+- ape->bar[i] = pci_iomap(dev, i, bar_min_len[i]);
+- if (!ape->bar[i]) {
+- printk(KERN_DEBUG "Could not map BAR #%d.\n", i);
+- rc = -1;
+- goto fail;
+- }
+- printk(KERN_DEBUG "BAR[%d] mapped at 0x%p with length %lu(/%lu).\n", i,
+- ape->bar[i], bar_min_len[i], bar_length);
+- }
+- /* successfully mapped all required BAR regions */
+- rc = 0;
+- goto success;
+-fail:
+- /* unmap any BARs that we did map */
+- unmap_bars(ape, dev);
+-success:
+- return rc;
+-}
+-
+-#if 0 /* not yet implemented fully FIXME add opcode */
+-static void __devinit rcslave_test(struct ape_dev *ape, struct pci_dev *dev)
+-{
+- u32 *rcslave_mem = (u32 *)ape->bar[APE_BAR_RCSLAVE];
+- u32 result = 0;
+- /** this number is assumed to be different each time this test runs */
+- u32 seed = (u32)jiffies;
+- u32 value = seed;
+- int i;
+-
+- /* write loop */
+- value = seed;
+- for (i = 1024; i < 32768 / 4 ; i++) {
+- printk(KERN_DEBUG "Writing 0x%08x to 0x%p.\n",
+- (u32)value, (void *)rcslave_mem + i);
+- iowrite32(value, rcslave_mem + i);
+- value++;
+- }
+- /* read-back loop */
+- value = seed;
+- for (i = 1024; i < 32768 / 4; i++) {
+- result = ioread32(rcslave_mem + i);
+- if (result != value) {
+- printk(KERN_DEBUG "Wrote 0x%08x to 0x%p, but read back 0x%08x.\n",
+- (u32)value, (void *)rcslave_mem + i, (u32)result);
+- break;
+- }
+- value++;
+- }
+-}
+-#endif
+-
+-/* obtain the 32 most significant (high) bits of a 32-bit or 64-bit address */
+-#define pci_dma_h(addr) ((addr >> 16) >> 16)
+-/* obtain the 32 least significant (low) bits of a 32-bit or 64-bit address */
+-#define pci_dma_l(addr) (addr & 0xffffffffUL)
+-
+-/* ape_fill_chdma_desc() - Fill a Altera PCI Express Chaining DMA descriptor
+- *
+- * @desc pointer to descriptor to be filled
+- * @addr root complex address
+- * @ep_addr end point address
+- * @len number of bytes, must be a multiple of 4.
+- */
+-static inline void ape_chdma_desc_set(struct ape_chdma_desc *desc, dma_addr_t addr, u32 ep_addr, int len)
+-{
+- BUG_ON(len & 3);
+- desc->w0 = cpu_to_le32(len / 4);
+- desc->ep_addr = cpu_to_le32(ep_addr);
+- desc->rc_addr_h = cpu_to_le32(pci_dma_h(addr));
+- desc->rc_addr_l = cpu_to_le32(pci_dma_l(addr));
+-}
+-
+-#if ALTPCIECHDMA_CDEV
+-/*
+- * ape_sg_to_chdma_table() - Create a device descriptor table from a scatterlist.
+- *
+- * The scatterlist must have been mapped by pci_map_sg(sgm->sgl).
+- *
+- * @sgl scatterlist.
+- * @nents Number of entries in the scatterlist.
+- * @first Start index in the scatterlist sgm->sgl.
+- * @ep_addr End Point address for the scatter/gather transfer.
+- * @desc pointer to first descriptor
+- *
+- * Returns Number of entries in the table on success, -1 on error.
+- */
+-static int ape_sg_to_chdma_table(struct scatterlist *sgl, int nents, int first, struct ape_chdma_desc *desc, u32 ep_addr)
+-{
+- int i = first, j = 0;
+- /* inspect first entry */
+- dma_addr_t addr = sg_dma_address(&sgl[i]);
+- unsigned int len = sg_dma_len(&sgl[i]);
+- /* contiguous block */
+- dma_addr_t cont_addr = addr;
+- unsigned int cont_len = len;
+- /* iterate over remaining entries */
+- for (; j < 25 && i < nents - 1; i++) {
+- /* bus address of next entry i + 1 */
+- dma_addr_t next = sg_dma_address(&sgl[i + 1]);
+- /* length of this entry i */
+- len = sg_dma_len(&sgl[i]);
+- printk(KERN_DEBUG "%04d: addr=0x%Lx length=0x%08x\n", i,
+- (unsigned long long)addr, len);
+- /* entry i + 1 is non-contiguous with entry i? */
+- if (next != addr + len) {
+- /* TODO create entry here (we could overwrite i) */
+- printk(KERN_DEBUG "%4d: cont_addr=0x%Lx cont_len=0x%08x\n", j,
+- (unsigned long long)cont_addr, cont_len);
+- /* set descriptor for contiguous transfer */
+- ape_chdma_desc_set(&desc[j], cont_addr, ep_addr, cont_len);
+- /* next end point memory address */
+- ep_addr += cont_len;
+- /* start new contiguous block */
+- cont_addr = next;
+- cont_len = 0;
+- j++;
+- }
+- /* add entry i + 1 to current contiguous block */
+- cont_len += len;
+- /* goto entry i + 1 */
+- addr = next;
+- }
+- /* TODO create entry here (we could overwrite i) */
+- printk(KERN_DEBUG "%04d: addr=0x%Lx length=0x%08x\n", i,
+- (unsigned long long)addr, len);
+- printk(KERN_DEBUG "%4d: cont_addr=0x%Lx length=0x%08x\n", j,
+- (unsigned long long)cont_addr, cont_len);
+- j++;
+- return j;
+-}
+-#endif
+-
+-/* compare buffers */
+-static inline int compare(u32 *p, u32 *q, int len)
+-{
+- int result = -1;
+- int fail = 0;
+- int i;
+- for (i = 0; i < len / 4; i++) {
+- if (*p == *q) {
+- /* every so many u32 words, show equals */
+- if ((i & 255) == 0)
+- printk(KERN_DEBUG "[%p] = 0x%08x [%p] = 0x%08x\n", p, *p, q, *q);
+- } else {
+- fail++;
+- /* show the first few miscompares */
+- if (fail < 10)
+- printk(KERN_DEBUG "[%p] = 0x%08x != [%p] = 0x%08x ?!\n", p, *p, q, *q);
+- /* but stop after a while */
+- else if (fail == 10)
+- printk(KERN_DEBUG "---more errors follow! not printed---\n");
+- else
+- /* stop compare after this many errors */
+- break;
+- }
+- p++;
+- q++;
+- }
+- if (!fail)
+- result = 0;
+- return result;
+-}
+-
+-/* dma_test() - Perform DMA loop back test to end point and back to root complex.
+- *
+- * Allocate a cache-coherent buffer in host memory, consisting of four pages.
+- *
+- * Fill the four memory pages such that each 32-bit word contains its own address.
+- *
+- * Now perform a loop back test, have the end point device copy the first buffer
+- * half to end point memory, then have it copy back into the second half.
+- *
+- * Create a descriptor table to copy the first buffer half into End Point
+- * memory. Instruct the End Point to do a DMA read using that table.
+- *
+- * Create a descriptor table to copy End Point memory to the second buffer
+- * half. Instruct the End Point to do a DMA write using that table.
+- *
+- * Compare results, fail or pass.
+- *
+- */
+-static int __devinit dma_test(struct ape_dev *ape, struct pci_dev *dev)
+-{
+- /* test result; guilty until proven innocent */
+- int result = -1;
+- /* the DMA read header sits at address 0x00 of the DMA engine BAR */
+- struct ape_chdma_header *write_header = (struct ape_chdma_header *)ape->bar[APE_BAR_HEADER];
+- /* the write DMA header sits after the read header at address 0x10 */
+- struct ape_chdma_header *read_header = write_header + 1;
+- /* virtual address of the allocated buffer */
+- u8 *buffer_virt = 0;
+- /* bus address of the allocated buffer */
+- dma_addr_t buffer_bus = 0;
+- int i, n = 0, irq_count;
+-
+- /* temporary value used to construct 32-bit data words */
+- u32 w;
+-
+- printk(KERN_DEBUG "bar_tests(), PAGE_SIZE = 0x%0x\n", (int)PAGE_SIZE);
+- printk(KERN_DEBUG "write_header = 0x%p.\n", write_header);
+- printk(KERN_DEBUG "read_header = 0x%p.\n", read_header);
+- printk(KERN_DEBUG "&write_header->w3 = 0x%p\n", &write_header->w3);
+- printk(KERN_DEBUG "&read_header->w3 = 0x%p\n", &read_header->w3);
+- printk(KERN_DEBUG "ape->table_virt = 0x%p.\n", ape->table_virt);
+-
+- if (!write_header || !read_header || !ape->table_virt)
+- goto fail;
+-
+- /* allocate and map coherently-cached memory for a DMA-able buffer */
+- /* @see Documentation/PCI/PCI-DMA-mapping.txt, near line 318 */
+- buffer_virt = (u8 *)pci_alloc_consistent(dev, PAGE_SIZE * 4, &buffer_bus);
+- if (!buffer_virt) {
+- printk(KERN_DEBUG "Could not allocate coherent DMA buffer.\n");
+- goto fail;
+- }
+- printk(KERN_DEBUG "Allocated cache-coherent DMA buffer (virtual address = %p, bus address = 0x%016llx).\n",
+- buffer_virt, (u64)buffer_bus);
+-
+- /* fill first half of buffer with its virtual address as data */
+- for (i = 0; i < 4 * PAGE_SIZE; i += 4)
+-#if 0
+- *(u32 *)(buffer_virt + i) = i / PAGE_SIZE + 1;
+-#else
+- *(u32 *)(buffer_virt + i) = (u32)(unsigned long)(buffer_virt + i);
+-#endif
+-#if 0
+- compare((u32 *)buffer_virt, (u32 *)(buffer_virt + 2 * PAGE_SIZE), 8192);
+-#endif
+-
+-#if 0
+- /* fill second half of buffer with zeroes */
+- for (i = 2 * PAGE_SIZE; i < 4 * PAGE_SIZE; i += 4)
+- *(u32 *)(buffer_virt + i) = 0;
+-#endif
+-
+- /* invalidate EPLAST, outside 0-255, 0xFADE is from the testbench */
+- ape->table_virt->w3 = cpu_to_le32(0x0000FADE);
+-
+- /* fill in first descriptor */
+- n = 0;
+- /* read 8192 bytes from RC buffer to EP address 4096 */
+- ape_chdma_desc_set(&ape->table_virt->desc[n], buffer_bus, 4096, 2 * PAGE_SIZE);
+-#if 1
+- for (i = 0; i < 255; i++)
+- ape_chdma_desc_set(&ape->table_virt->desc[i], buffer_bus, 4096, 2 * PAGE_SIZE);
+- /* index of last descriptor */
+- n = i - 1;
+-#endif
+-#if 0
+- /* fill in next descriptor */
+- n++;
+- /* read 1024 bytes from RC buffer to EP address 4096 + 1024 */
+- ape_chdma_desc_set(&ape->table_virt->desc[n], buffer_bus + 1024, 4096 + 1024, 1024);
+-#endif
+-
+-#if 1
+- /* enable MSI after the last descriptor is completed */
+- if (ape->msi_enabled)
+- ape->table_virt->desc[n].w0 |= cpu_to_le32(1UL << 16)/*local MSI*/;
+-#endif
+-#if 0
+- /* dump descriptor table for debugging */
+- printk(KERN_DEBUG "Descriptor Table (Read, in Root Complex Memory, # = %d)\n", n + 1);
+- for (i = 0; i < 4 + (n + 1) * 4; i += 4) {
+- u32 *p = (u32 *)ape->table_virt;
+- p += i;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (LEN=0x%x)\n", (u32)p, (u32)p & 15, *p, 4 * le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (EPA=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (RCH=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (RCL=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- }
+-#endif
+- /* set available number of descriptors in table */
+- w = (u32)(n + 1);
+- w |= (1UL << 18)/*global EPLAST_EN*/;
+-#if 0
+- if (ape->msi_enabled)
+- w |= (1UL << 17)/*global MSI*/;
+-#endif
+- printk(KERN_DEBUG "writing 0x%08x to 0x%p\n", w, (void *)&read_header->w0);
+- iowrite32(w, &read_header->w0);
+-
+- /* write table address (higher 32-bits) */
+- printk(KERN_DEBUG "writing 0x%08x to 0x%p\n", (u32)((ape->table_bus >> 16) >> 16), (void *)&read_header->bdt_addr_h);
+- iowrite32(pci_dma_h(ape->table_bus), &read_header->bdt_addr_h);
+-
+- /* write table address (lower 32-bits) */
+- printk(KERN_DEBUG "writing 0x%08x to 0x%p\n", (u32)(ape->table_bus & 0xffffffffUL), (void *)&read_header->bdt_addr_l);
+- iowrite32(pci_dma_l(ape->table_bus), &read_header->bdt_addr_l);
+-
+- /* memory write barrier */
+- wmb();
+- printk(KERN_DEBUG "Flush posted writes\n");
+- /** FIXME Add dummy read to flush posted writes but need a readable location! */
+-#if 0
+- (void)ioread32();
+-#endif
+-
+- /* remember IRQ count before the transfer */
+- irq_count = ape->irq_count;
+- /* write number of descriptors - this starts the DMA */
+- printk(KERN_DEBUG "\nStart DMA read\n");
+- printk(KERN_DEBUG "writing 0x%08x to 0x%p\n", (u32)n, (void *)&read_header->w3);
+- iowrite32(n, &read_header->w3);
+- printk(KERN_DEBUG "EPLAST = %lu\n", le32_to_cpu(*(u32 *)&ape->table_virt->w3) & 0xffffUL);
+-
+- /** memory write barrier */
+- wmb();
+- /* dummy read to flush posted writes */
+- /* FIXME Need a readable location! */
+-#if 0
+- (void)ioread32();
+-#endif
+- printk(KERN_DEBUG "POLL FOR READ:\n");
+- /* poll for chain completion, 1000 times 1 millisecond */
+- for (i = 0; i < 100; i++) {
+- volatile u32 *p = &ape->table_virt->w3;
+- u32 eplast = le32_to_cpu(*p) & 0xffffUL;
+- printk(KERN_DEBUG "EPLAST = %u, n = %d\n", eplast, n);
+- if (eplast == n) {
+- printk(KERN_DEBUG "DONE\n");
+- /* print IRQ count before the transfer */
+- printk(KERN_DEBUG "#IRQs during transfer: %d\n", ape->irq_count - irq_count);
+- break;
+- }
+- udelay(100);
+- }
+-
+- /* invalidate EPLAST, outside 0-255, 0xFADE is from the testbench */
+- ape->table_virt->w3 = cpu_to_le32(0x0000FADE);
+-
+- /* setup first descriptor */
+- n = 0;
+- ape_chdma_desc_set(&ape->table_virt->desc[n], buffer_bus + 8192, 4096, 2 * PAGE_SIZE);
+-#if 1
+- for (i = 0; i < 255; i++)
+- ape_chdma_desc_set(&ape->table_virt->desc[i], buffer_bus + 8192, 4096, 2 * PAGE_SIZE);
+-
+- /* index of last descriptor */
+- n = i - 1;
+-#endif
+-#if 1 /* test variable, make a module option later */
+- if (ape->msi_enabled)
+- ape->table_virt->desc[n].w0 |= cpu_to_le32(1UL << 16)/*local MSI*/;
+-#endif
+-#if 0
+- /* dump descriptor table for debugging */
+- printk(KERN_DEBUG "Descriptor Table (Write, in Root Complex Memory, # = %d)\n", n + 1);
+- for (i = 0; i < 4 + (n + 1) * 4; i += 4) {
+- u32 *p = (u32 *)ape->table_virt;
+- p += i;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (LEN=0x%x)\n", (u32)p, (u32)p & 15, *p, 4 * le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (EPA=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (RCH=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- p++;
+- printk(KERN_DEBUG "0x%08x/0x%02x: 0x%08x (RCL=0x%x)\n", (u32)p, (u32)p & 15, *p, le32_to_cpu(*p));
+- }
+-#endif
+-
+- /* set number of available descriptors in the table */
+- w = (u32)(n + 1);
+- /* enable updates of eplast for each descriptor completion */
+- w |= (u32)(1UL << 18)/*global EPLAST_EN*/;
+-#if 0 /* test variable, make a module option later */
+- /* enable MSI for each descriptor completion */
+- if (ape->msi_enabled)
+- w |= (1UL << 17)/*global MSI*/;
+-#endif
+- iowrite32(w, &write_header->w0);
+- iowrite32(pci_dma_h(ape->table_bus), &write_header->bdt_addr_h);
+- iowrite32(pci_dma_l(ape->table_bus), &write_header->bdt_addr_l);
+-
+- /** memory write barrier and flush posted writes */
+- wmb();
+- /* dummy read to flush posted writes */
+- /* FIXME Need a readable location! */
+-#if 0
+- (void)ioread32();
+-#endif
+- irq_count = ape->irq_count;
+-
+- printk(KERN_DEBUG "\nStart DMA write\n");
+- iowrite32(n, &write_header->w3);
+-
+- /** memory write barrier */
+- wmb();
+- /** dummy read to flush posted writes */
+- /* (void) ioread32(); */
+-
+- printk(KERN_DEBUG "POLL FOR WRITE:\n");
+- /* poll for completion, 1000 times 1 millisecond */
+- for (i = 0; i < 100; i++) {
+- volatile u32 *p = &ape->table_virt->w3;
+- u32 eplast = le32_to_cpu(*p) & 0xffffUL;
+- printk(KERN_DEBUG "EPLAST = %u, n = %d\n", eplast, n);
+- if (eplast == n) {
+- printk(KERN_DEBUG "DONE\n");
+- /* print IRQ count before the transfer */
+- printk(KERN_DEBUG "#IRQs during transfer: %d\n", ape->irq_count - irq_count);
+- break;
+- }
+- udelay(100);
+- }
+- /* soft-reset DMA write engine */
+- iowrite32(0x0000ffffUL, &write_header->w0);
+- /* soft-reset DMA read engine */
+- iowrite32(0x0000ffffUL, &read_header->w0);
+-
+- /** memory write barrier */
+- wmb();
+- /* dummy read to flush posted writes */
+- /* FIXME Need a readable location! */
+-#if 0
+- (void)ioread32();
+-#endif
+- /* compare first half of buffer with second half, should be identical */
+- result = compare((u32 *)buffer_virt, (u32 *)(buffer_virt + 2 * PAGE_SIZE), 8192);
+- printk(KERN_DEBUG "DMA loop back test %s.\n", result ? "FAILED" : "PASSED");
+-
+- pci_free_consistent(dev, 4 * PAGE_SIZE, buffer_virt, buffer_bus);
+-fail:
+- printk(KERN_DEBUG "bar_tests() end, result %d\n", result);
+- return result;
+-}
+-
+-/* Called when the PCI sub system thinks we can control the given device.
+- * Inspect if we can support the device and if so take control of it.
+- *
+- * Return 0 when we have taken control of the given device.
+- *
+- * - allocate board specific bookkeeping
+- * - allocate coherently-mapped memory for the descriptor table
+- * - enable the board
+- * - verify board revision
+- * - request regions
+- * - query DMA mask
+- * - obtain and request irq
+- * - map regions into kernel address space
+- */
+-static int __devinit probe(struct pci_dev *dev, const struct pci_device_id *id)
+-{
+- int rc = 0;
+- struct ape_dev *ape = NULL;
+- u8 irq_pin, irq_line;
+- printk(KERN_DEBUG "probe(dev = 0x%p, pciid = 0x%p)\n", dev, id);
+-
+- /* allocate memory for per-board book keeping */
+- ape = kzalloc(sizeof(struct ape_dev), GFP_KERNEL);
+- if (!ape) {
+- printk(KERN_DEBUG "Could not kzalloc()ate memory.\n");
+- goto err_ape;
+- }
+- ape->pci_dev = dev;
+- dev_set_drvdata(&dev->dev, ape);
+- printk(KERN_DEBUG "probe() ape = 0x%p\n", ape);
+-
+- printk(KERN_DEBUG "sizeof(struct ape_chdma_table) = %d.\n",
+- (int)sizeof(struct ape_chdma_table));
+- /* the reference design has a size restriction on the table size */
+- BUG_ON(sizeof(struct ape_chdma_table) > APE_CHDMA_TABLE_SIZE);
+-
+- /* allocate and map coherently-cached memory for a descriptor table */
+- /* @see LDD3 page 446 */
+- ape->table_virt = (struct ape_chdma_table *)pci_alloc_consistent(dev,
+- APE_CHDMA_TABLE_SIZE, &ape->table_bus);
+- /* could not allocate table? */
+- if (!ape->table_virt) {
+- printk(KERN_DEBUG "Could not dma_alloc()ate_coherent memory.\n");
+- goto err_table;
+- }
+-
+- printk(KERN_DEBUG "table_virt = %p, table_bus = 0x%16llx.\n",
+- ape->table_virt, (u64)ape->table_bus);
+-
+- /* enable device */
+- rc = pci_enable_device(dev);
+- if (rc) {
+- printk(KERN_DEBUG "pci_enable_device() failed\n");
+- goto err_enable;
+- }
+-
+- /* enable bus master capability on device */
+- pci_set_master(dev);
+- /* enable message signaled interrupts */
+- rc = pci_enable_msi(dev);
+- /* could not use MSI? */
+- if (rc) {
+- /* resort to legacy interrupts */
+- printk(KERN_DEBUG "Could not enable MSI interrupting.\n");
+- ape->msi_enabled = 0;
+- /* MSI enabled, remember for cleanup */
+- } else {
+- printk(KERN_DEBUG "Enabled MSI interrupting.\n");
+- ape->msi_enabled = 1;
+- }
+-
+- pci_read_config_byte(dev, PCI_REVISION_ID, &ape->revision);
+-#if 0 /* example */
+- /* (for example) this driver does not support revision 0x42 */
+- if (ape->revision == 0x42) {
+- printk(KERN_DEBUG "Revision 0x42 is not supported by this driver.\n");
+- rc = -ENODEV;
+- goto err_rev;
+- }
+-#endif
+- /** XXX check for native or legacy PCIe endpoint? */
+-
+- rc = pci_request_regions(dev, DRV_NAME);
+- /* could not request all regions? */
+- if (rc) {
+- /* assume device is in use (and do not disable it later!) */
+- ape->in_use = 1;
+- goto err_regions;
+- }
+- ape->got_regions = 1;
+-
+-#if 1 /* @todo For now, disable 64-bit, because I do not understand the implications (DAC!) */
+- /* query for DMA transfer */
+- /* @see Documentation/PCI/PCI-DMA-mapping.txt */
+- if (!pci_set_dma_mask(dev, DMA_BIT_MASK(64))) {
+- pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64));
+- /* use 64-bit DMA */
+- printk(KERN_DEBUG "Using a 64-bit DMA mask.\n");
+- } else
+-#endif
+- if (!pci_set_dma_mask(dev, DMA_BIT_MASK(32))) {
+- printk(KERN_DEBUG "Could not set 64-bit DMA mask.\n");
+- pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32));
+- /* use 32-bit DMA */
+- printk(KERN_DEBUG "Using a 32-bit DMA mask.\n");
+- } else {
+- printk(KERN_DEBUG "No suitable DMA possible.\n");
+- /** @todo Choose proper error return code */
+- rc = -1;
+- goto err_mask;
+- }
+-
+- rc = pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &irq_pin);
+- /* could not read? */
+- if (rc)
+- goto err_irq;
+- printk(KERN_DEBUG "IRQ pin #%d (0=none, 1=INTA#...4=INTD#).\n", irq_pin);
+-
+- /* @see LDD3, page 318 */
+- rc = pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq_line);
+- /* could not read? */
+- if (rc) {
+- printk(KERN_DEBUG "Could not query PCI_INTERRUPT_LINE, error %d\n", rc);
+- goto err_irq;
+- }
+- printk(KERN_DEBUG "IRQ line #%d.\n", irq_line);
+-#if 1
+- irq_line = dev->irq;
+- /* @see LDD3, page 259 */
+- rc = request_irq(irq_line, altpciechdma_isr, IRQF_SHARED, DRV_NAME, (void *)ape);
+- if (rc) {
+- printk(KERN_DEBUG "Could not request IRQ #%d, error %d\n", irq_line, rc);
+- ape->irq_line = -1;
+- goto err_irq;
+- }
+- /* remember which irq we allocated */
+- ape->irq_line = (int)irq_line;
+- printk(KERN_DEBUG "Successfully requested IRQ #%d with dev_id 0x%p\n", irq_line, ape);
+-#endif
+- /* show BARs */
+- scan_bars(ape, dev);
+- /* map BARs */
+- rc = map_bars(ape, dev);
+- if (rc)
+- goto err_map;
+-#if ALTPCIECHDMA_CDEV
+- /* initialize character device */
+- rc = sg_init(ape);
+- if (rc)
+- goto err_cdev;
+-#endif
+- /* perform DMA engines loop back test */
+- rc = dma_test(ape, dev);
+- (void)rc;
+- /* successfully took the device */
+- rc = 0;
+- printk(KERN_DEBUG "probe() successful.\n");
+- goto end;
+-#if ALTPCIECHDMA_CDEV
+-err_cdev:
+- /* unmap the BARs */
+- unmap_bars(ape, dev);
+-#endif
+-err_map:
+- /* free allocated irq */
+- if (ape->irq_line >= 0)
+- free_irq(ape->irq_line, (void *)ape);
+-err_irq:
+- if (ape->msi_enabled)
+- pci_disable_msi(dev);
+- /* disable the device iff it is not in use */
+- if (!ape->in_use)
+- pci_disable_device(dev);
+- if (ape->got_regions)
+- pci_release_regions(dev);
+-err_mask:
+-err_regions:
+-/*err_rev:*/
+-/* clean up everything before device enable() */
+-err_enable:
+- if (ape->table_virt)
+- pci_free_consistent(dev, APE_CHDMA_TABLE_SIZE, ape->table_virt, ape->table_bus);
+-/* clean up everything before allocating descriptor table */
+-err_table:
+- if (ape)
+- kfree(ape);
+-err_ape:
+-end:
+- return rc;
+-}
+-
+-static void __devexit remove(struct pci_dev *dev)
+-{
+- struct ape_dev *ape = dev_get_drvdata(&dev->dev);
+-
+- printk(KERN_DEBUG "remove(0x%p)\n", dev);
+- printk(KERN_DEBUG "remove(dev = 0x%p) where ape = 0x%p\n", dev, ape);
+-
+- /* remove character device */
+-#if ALTPCIECHDMA_CDEV
+- sg_exit(ape);
+-#endif
+-
+- if (ape->table_virt)
+- pci_free_consistent(dev, APE_CHDMA_TABLE_SIZE, ape->table_virt, ape->table_bus);
+-
+- /* free IRQ
+- * @see LDD3 page 279
+- */
+- if (ape->irq_line >= 0) {
+- printk(KERN_DEBUG "Freeing IRQ #%d for dev_id 0x%08lx.\n",
+- ape->irq_line, (unsigned long)ape);
+- free_irq(ape->irq_line, (void *)ape);
+- }
+- /* MSI was enabled? */
+- if (ape->msi_enabled) {
+- /* Disable MSI @see Documentation/MSI-HOWTO.txt */
+- pci_disable_msi(dev);
+- ape->msi_enabled = 0;
+- }
+- /* unmap the BARs */
+- unmap_bars(ape, dev);
+- if (!ape->in_use)
+- pci_disable_device(dev);
+- if (ape->got_regions)
+- /* to be called after device disable */
+- pci_release_regions(dev);
+-}
+-
+-#if ALTPCIECHDMA_CDEV
+-
+-/*
+- * Called when the device goes from unused to used.
+- */
+-static int sg_open(struct inode *inode, struct file *file)
+-{
+- struct ape_dev *ape;
+- printk(KERN_DEBUG DRV_NAME "_open()\n");
+- /* pointer to containing data structure of the character device inode */
+- ape = container_of(inode->i_cdev, struct ape_dev, cdev);
+- /* create a reference to our device state in the opened file */
+- file->private_data = ape;
+- /* create virtual memory mapper */
+- ape->sgm = sg_create_mapper(MAX_CHDMA_SIZE);
+- return 0;
+-}
+-
+-/*
+- * Called when the device goes from used to unused.
+- */
+-static int sg_close(struct inode *inode, struct file *file)
+-{
+- /* fetch device specific data stored earlier during open */
+- struct ape_dev *ape = (struct ape_dev *)file->private_data;
+- printk(KERN_DEBUG DRV_NAME "_close()\n");
+- /* destroy virtual memory mapper */
+- sg_destroy_mapper(ape->sgm);
+- return 0;
+-}
+-
+-static ssize_t sg_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+-{
+- /* fetch device specific data stored earlier during open */
+- struct ape_dev *ape = (struct ape_dev *)file->private_data;
+- (void)ape;
+- printk(KERN_DEBUG DRV_NAME "_read(buf=0x%p, count=%lld, pos=%llu)\n", buf, (s64)count, (u64)*pos);
+- return count;
+-}
+-
+-/* sg_write() - Write to the device
+- *
+- * @buf userspace buffer
+- * @count number of bytes in the userspace buffer
+- *
+- * Iterate over the userspace buffer, taking at most 255 * PAGE_SIZE bytes for
+- * each DMA transfer.
+- * For each transfer, get the user pages, build a sglist, map, build a
+- * descriptor table. submit the transfer. wait for the interrupt handler
+- * to wake us on completion.
+- */
+-static ssize_t sg_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
+-{
+- int hwnents, tents;
+- size_t transfer_len, remaining = count, done = 0;
+- u64 transfer_addr = (u64)buf;
+- /* fetch device specific data stored earlier during open */
+- struct ape_dev *ape = (struct ape_dev *)file->private_data;
+- printk(KERN_DEBUG DRV_NAME "_write(buf=0x%p, count=%lld, pos=%llu)\n",
+- buf, (s64)count, (u64)*pos);
+- /* TODO transfer boundaries at PAGE_SIZE granularity */
+- while (remaining > 0) {
+- /* limit DMA transfer size */
+- transfer_len = (remaining < APE_CHDMA_MAX_TRANSFER_LEN) ? remaining :
+- APE_CHDMA_MAX_TRANSFER_LEN;
+- /* get all user space buffer pages and create a scattergather list */
+- sgm_map_user_pages(ape->sgm, transfer_addr, transfer_len, 0/*read from userspace*/);
+- printk(KERN_DEBUG DRV_NAME "mapped_pages=%d\n", ape->sgm->mapped_pages);
+- /* map all entries in the scattergather list */
+- hwnents = pci_map_sg(ape->pci_dev, ape->sgm->sgl, ape->sgm->mapped_pages, DMA_TO_DEVICE);
+- printk(KERN_DEBUG DRV_NAME "hwnents=%d\n", hwnents);
+- /* build device descriptor tables and submit them to the DMA engine */
+- tents = ape_sg_to_chdma_table(ape->sgm->sgl, hwnents, 0, &ape->table_virt->desc[0], 4096);
+- printk(KERN_DEBUG DRV_NAME "tents=%d\n", hwnents);
+-#if 0
+- while (tables) {
+- /* TODO build table */
+- /* TODO submit table to the device */
+- /* if engine stopped and unfinished work then start engine */
+- }
+- put ourselves on wait queue
+-#endif
+-
+- dma_unmap_sg(NULL, ape->sgm->sgl, ape->sgm->mapped_pages, DMA_TO_DEVICE);
+- /* dirty and free the pages */
+- sgm_unmap_user_pages(ape->sgm, 1/*dirtied*/);
+- /* book keeping */
+- transfer_addr += transfer_len;
+- remaining -= transfer_len;
+- done += transfer_len;
+- }
+- return done;
+-}
+-
+-/*
+- * character device file operations
+- */
+-static const struct file_operations sg_fops = {
+- .owner = THIS_MODULE,
+- .open = sg_open,
+- .release = sg_close,
+- .read = sg_read,
+- .write = sg_write,
+-};
+-
+-/* sg_init() - Initialize character device
+- *
+- * XXX Should ideally be tied to the device, on device probe, not module init.
+- */
+-static int sg_init(struct ape_dev *ape)
+-{
+- int rc;
+- printk(KERN_DEBUG DRV_NAME " sg_init()\n");
+- /* allocate a dynamically allocated character device node */
+- rc = alloc_chrdev_region(&ape->cdevno, 0/*requested minor*/, 1/*count*/, DRV_NAME);
+- /* allocation failed? */
+- if (rc < 0) {
+- printk("alloc_chrdev_region() = %d\n", rc);
+- goto fail_alloc;
+- }
+- /* couple the device file operations to the character device */
+- cdev_init(&ape->cdev, &sg_fops);
+- ape->cdev.owner = THIS_MODULE;
+- /* bring character device live */
+- rc = cdev_add(&ape->cdev, ape->cdevno, 1/*count*/);
+- if (rc < 0) {
+- printk("cdev_add() = %d\n", rc);
+- goto fail_add;
+- }
+- printk(KERN_DEBUG "altpciechdma = %d:%d\n", MAJOR(ape->cdevno), MINOR(ape->cdevno));
+- return 0;
+-fail_add:
+- /* free the dynamically allocated character device node */
+- unregister_chrdev_region(ape->cdevno, 1/*count*/);
+-fail_alloc:
+- return -1;
+-}
+-
+-/* sg_exit() - Cleanup character device
+- *
+- * XXX Should ideally be tied to the device, on device remove, not module exit.
+- */
+-
+-static void sg_exit(struct ape_dev *ape)
+-{
+- printk(KERN_DEBUG DRV_NAME " sg_exit()\n");
+- /* remove the character device */
+- cdev_del(&ape->cdev);
+- /* free the dynamically allocated character device node */
+- unregister_chrdev_region(ape->cdevno, 1/*count*/);
+-}
+-
+-#endif /* ALTPCIECHDMA_CDEV */
+-
+-/* used to register the driver with the PCI kernel sub system
+- * @see LDD3 page 311
+- */
+-static struct pci_driver pci_driver = {
+- .name = DRV_NAME,
+- .id_table = ids,
+- .probe = probe,
+- .remove = __devexit_p(remove),
+- /* resume, suspend are optional */
+-};
+-
+-/**
+- * alterapciechdma_init() - Module initialization, registers devices.
+- */
+-static int __init alterapciechdma_init(void)
+-{
+- int rc = 0;
+- printk(KERN_DEBUG DRV_NAME " init(), built at " __DATE__ " " __TIME__ "\n");
+- /* register this driver with the PCI bus driver */
+- rc = pci_register_driver(&pci_driver);
+- if (rc < 0)
+- return rc;
+- return 0;
+-}
+-
+-/**
+- * alterapciechdma_init() - Module cleanup, unregisters devices.
+- */
+-static void __exit alterapciechdma_exit(void)
+-{
+- printk(KERN_DEBUG DRV_NAME " exit(), built at " __DATE__ " " __TIME__ "\n");
+- /* unregister this driver from the PCI bus driver */
+- pci_unregister_driver(&pci_driver);
+-}
+-
+-MODULE_LICENSE("GPL");
+-
+-module_init(alterapciechdma_init);
+-module_exit(alterapciechdma_exit);
+-
+--- a/drivers/staging/altpciechdma/Kconfig
++++ /dev/null
+@@ -1,10 +0,0 @@
+-config ALTERA_PCIE_CHDMA
+- tristate "Altera PCI Express Chaining DMA driver"
+- depends on PCI
+- default N
+- ---help---
+- A reference driver that exercises the Chaining DMA logic reference
+- design generated along the Altera FPGA PCI Express soft or hard core,
+- only if instantiated using the MegaWizard, not the SOPC builder, of
+- Quartus 8.1.
+-
+--- a/drivers/staging/altpciechdma/Makefile
++++ /dev/null
+@@ -1,2 +0,0 @@
+-obj-$(CONFIG_ALTERA_PCIE_CHDMA) += altpciechdma.o
+-
+--- a/drivers/staging/altpciechdma/TODO
++++ /dev/null
+@@ -1,15 +0,0 @@
+-DONE:
+- - functionality similar to logic testbench
+-
+-TODO:
+- - checkpatch.pl cleanups.
+- - keep state of DMA engines.
+- - keep data structure that keeps state of each transfer.
+- - interrupt handler should iterate over outstanding descriptor tables.
+- - complete userspace cdev to read/write using the DMA engines.
+- - split off the DMA support functions in a module, re-usable by custom
+- drivers.
+-
+-Please coordinate work with, and send patches to
+-Leon Woestenberg <leon@sidebranch.com>
+-
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -71,8 +71,6 @@ source "drivers/staging/asus_oled/Kconfi
+
+ source "drivers/staging/panel/Kconfig"
+
+-source "drivers/staging/altpciechdma/Kconfig"
+-
+ source "drivers/staging/rtl8187se/Kconfig"
+
+ source "drivers/staging/rtl8192su/Kconfig"
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -18,7 +18,6 @@ obj-$(CONFIG_RT2870) += rt2870/
+ obj-$(CONFIG_COMEDI) += comedi/
+ obj-$(CONFIG_ASUS_OLED) += asus_oled/
+ obj-$(CONFIG_PANEL) += panel/
+-obj-$(CONFIG_ALTERA_PCIE_CHDMA) += altpciechdma/
+ obj-$(CONFIG_R8187SE) += rtl8187se/
+ obj-$(CONFIG_RTL8192SU) += rtl8192su/
+ obj-$(CONFIG_RTL8192U) += rtl8192u/
diff --git a/staging/staging-p9auth-remove-driver-from-tree.patch b/staging/staging-p9auth-remove-driver-from-tree.patch
new file mode 100644
index 00000000000000..daf1cc4f3550c3
--- /dev/null
+++ b/staging/staging-p9auth-remove-driver-from-tree.patch
@@ -0,0 +1,467 @@
+From foo@baz Tue Dec 22 16:35:21 PST 2009
+Date: Tue, 22 Dec 2009 16:35:21 -0800
+To: Greg KH <greg@kroah.com>
+From: Greg Kroah-Hartman <gregkh@suse.de>
+Subject: Staging: p9auth: remove driver from tree
+
+No one seems to be maintaining this anymore, and it is not on any
+track to be merged to mainline.
+
+Cc: Ashwin Ganti <ashwin.ganti@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/staging/Kconfig | 2
+ drivers/staging/Makefile | 1
+ drivers/staging/p9auth/Kconfig | 9
+ drivers/staging/p9auth/Makefile | 1
+ drivers/staging/p9auth/p9auth.c | 408 ----------------------------------------
+ 5 files changed, 421 deletions(-)
+
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -91,8 +91,6 @@ source "drivers/staging/pohmelfs/Kconfig
+
+ source "drivers/staging/phison/Kconfig"
+
+-source "drivers/staging/p9auth/Kconfig"
+-
+ source "drivers/staging/line6/Kconfig"
+
+ source "drivers/gpu/drm/vmwgfx/Kconfig"
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -28,7 +28,6 @@ obj-$(CONFIG_TRANZPORT) += frontier/
+ obj-$(CONFIG_DREAM) += dream/
+ obj-$(CONFIG_POHMELFS) += pohmelfs/
+ obj-$(CONFIG_IDE_PHISON) += phison/
+-obj-$(CONFIG_PLAN9AUTH) += p9auth/
+ obj-$(CONFIG_LINE6_USB) += line6/
+ obj-$(CONFIG_USB_SERIAL_QUATECH2) += serqt_usb2/
+ obj-$(CONFIG_USB_SERIAL_QUATECH_USB2) += quatech_usb2/
+--- a/drivers/staging/p9auth/Kconfig
++++ /dev/null
+@@ -1,9 +0,0 @@
+-config PLAN9AUTH
+- tristate "Plan 9 style capability device implementation"
+- default n
+- depends on CRYPTO
+- help
+- This module implements the Plan 9 style capability device.
+-
+- To compile this driver as a module, choose
+- M here: the module will be called p9auth.
+--- a/drivers/staging/p9auth/Makefile
++++ /dev/null
+@@ -1 +0,0 @@
+-obj-$(CONFIG_PLAN9AUTH) += p9auth.o
+--- a/drivers/staging/p9auth/p9auth.c
++++ /dev/null
+@@ -1,408 +0,0 @@
+-/*
+- * Plan 9 style capability device implementation for the Linux Kernel
+- *
+- * Copyright 2008, 2009 Ashwin Ganti <ashwin.ganti@gmail.com>
+- *
+- * Released under the GPLv2
+- *
+- */
+-#include <linux/init.h>
+-#include <linux/kernel.h>
+-#include <linux/moduleparam.h>
+-#include <linux/slab.h>
+-#include <linux/fs.h>
+-#include <linux/errno.h>
+-#include <linux/fcntl.h>
+-#include <linux/cdev.h>
+-#include <linux/uaccess.h>
+-#include <linux/list.h>
+-#include <linux/mm.h>
+-#include <linux/string.h>
+-#include <linux/crypto.h>
+-#include <linux/highmem.h>
+-#include <linux/scatterlist.h>
+-#include <linux/sched.h>
+-#include <linux/cred.h>
+-
+-#ifndef CAP_MAJOR
+-#define CAP_MAJOR 0
+-#endif
+-
+-#ifndef CAP_NR_DEVS
+-#define CAP_NR_DEVS 2 /* caphash and capuse */
+-#endif
+-
+-#ifndef CAP_NODE_SIZE
+-#define CAP_NODE_SIZE 20
+-#endif
+-
+-#define MAX_DIGEST_SIZE 20
+-
+-struct cap_node {
+- char data[CAP_NODE_SIZE];
+- struct list_head list;
+-};
+-
+-struct cap_dev {
+- struct cap_node *head;
+- int node_size;
+- unsigned long size;
+- struct semaphore sem;
+- struct cdev cdev;
+-};
+-
+-static int cap_major = CAP_MAJOR;
+-static int cap_minor;
+-static int cap_nr_devs = CAP_NR_DEVS;
+-static int cap_node_size = CAP_NODE_SIZE;
+-
+-module_param(cap_major, int, S_IRUGO);
+-module_param(cap_minor, int, S_IRUGO);
+-module_param(cap_nr_devs, int, S_IRUGO);
+-
+-MODULE_AUTHOR("Ashwin Ganti");
+-MODULE_LICENSE("GPL");
+-
+-static struct cap_dev *cap_devices;
+-
+-static void hexdump(unsigned char *buf, unsigned int len)
+-{
+- while (len--)
+- printk("%02x", *buf++);
+- printk("\n");
+-}
+-
+-static char *cap_hash(char *plain_text, unsigned int plain_text_size,
+- char *key, unsigned int key_size)
+-{
+- struct scatterlist sg;
+- char *result;
+- struct crypto_hash *tfm;
+- struct hash_desc desc;
+- int ret;
+-
+- tfm = crypto_alloc_hash("hmac(sha1)", 0, CRYPTO_ALG_ASYNC);
+- if (IS_ERR(tfm)) {
+- printk(KERN_ERR
+- "failed to load transform for hmac(sha1): %ld\n",
+- PTR_ERR(tfm));
+- return NULL;
+- }
+-
+- desc.tfm = tfm;
+- desc.flags = 0;
+-
+- result = kzalloc(MAX_DIGEST_SIZE, GFP_KERNEL);
+- if (!result) {
+- printk(KERN_ERR "out of memory!\n");
+- goto out;
+- }
+-
+- sg_set_buf(&sg, plain_text, plain_text_size);
+-
+- ret = crypto_hash_setkey(tfm, key, key_size);
+- if (ret) {
+- printk(KERN_ERR "setkey() failed ret=%d\n", ret);
+- kfree(result);
+- result = NULL;
+- goto out;
+- }
+-
+- ret = crypto_hash_digest(&desc, &sg, plain_text_size, result);
+- if (ret) {
+- printk(KERN_ERR "digest () failed ret=%d\n", ret);
+- kfree(result);
+- result = NULL;
+- goto out;
+- }
+-
+- printk(KERN_DEBUG "crypto hash digest size %d\n",
+- crypto_hash_digestsize(tfm));
+- hexdump(result, MAX_DIGEST_SIZE);
+-
+-out:
+- crypto_free_hash(tfm);
+- return result;
+-}
+-
+-static int cap_trim(struct cap_dev *dev)
+-{
+- struct cap_node *tmp;
+- struct list_head *pos, *q;
+- if (dev->head != NULL) {
+- list_for_each_safe(pos, q, &(dev->head->list)) {
+- tmp = list_entry(pos, struct cap_node, list);
+- list_del(pos);
+- kfree(tmp);
+- }
+- }
+- return 0;
+-}
+-
+-static int cap_open(struct inode *inode, struct file *filp)
+-{
+- struct cap_dev *dev;
+- dev = container_of(inode->i_cdev, struct cap_dev, cdev);
+- filp->private_data = dev;
+-
+- /* trim to 0 the length of the device if open was write-only */
+- if ((filp->f_flags & O_ACCMODE) == O_WRONLY) {
+- if (down_interruptible(&dev->sem))
+- return -ERESTARTSYS;
+- cap_trim(dev);
+- up(&dev->sem);
+- }
+- /* initialise the head if it is NULL */
+- if (dev->head == NULL) {
+- dev->head = kmalloc(sizeof(struct cap_node), GFP_KERNEL);
+- INIT_LIST_HEAD(&(dev->head->list));
+- }
+- return 0;
+-}
+-
+-static int cap_release(struct inode *inode, struct file *filp)
+-{
+- return 0;
+-}
+-
+-static ssize_t cap_write(struct file *filp, const char __user *buf,
+- size_t count, loff_t *f_pos)
+-{
+- struct cap_node *node_ptr, *tmp;
+- struct list_head *pos;
+- struct cap_dev *dev = filp->private_data;
+- ssize_t retval = -ENOMEM;
+- struct cred *new;
+- int len, target_int, source_int, flag = 0;
+- char *user_buf, *user_buf_running, *source_user, *target_user,
+- *rand_str, *hash_str, *result;
+-
+- if (down_interruptible(&dev->sem))
+- return -ERESTARTSYS;
+-
+- user_buf_running = NULL;
+- hash_str = NULL;
+- node_ptr = kmalloc(sizeof(struct cap_node), GFP_KERNEL);
+- user_buf = kzalloc(count+1, GFP_KERNEL);
+- if (!node_ptr || !user_buf)
+- goto out;
+-
+- if (copy_from_user(user_buf, buf, count)) {
+- retval = -EFAULT;
+- goto out;
+- }
+-
+- /*
+- * If the minor number is 0 ( /dev/caphash ) then simply add the
+- * hashed capability supplied by the user to the list of hashes
+- */
+- if (0 == iminor(filp->f_dentry->d_inode)) {
+- if (count > CAP_NODE_SIZE) {
+- retval = -EINVAL;
+- goto out;
+- }
+- printk(KERN_INFO "Capability being written to /dev/caphash : \n");
+- hexdump(user_buf, count);
+- memcpy(node_ptr->data, user_buf, count);
+- list_add(&(node_ptr->list), &(dev->head->list));
+- node_ptr = NULL;
+- } else {
+- char *tmpu;
+- if (!cap_devices[0].head ||
+- list_empty(&(cap_devices[0].head->list))) {
+- retval = -EINVAL;
+- goto out;
+- }
+- /*
+- * break the supplied string into tokens with @ as the
+- * delimiter If the string is "user1@user2@randomstring" we
+- * need to split it and hash 'user1@user2' using 'randomstring'
+- * as the key.
+- */
+- tmpu = user_buf_running = kstrdup(user_buf, GFP_KERNEL);
+- source_user = strsep(&tmpu, "@");
+- target_user = strsep(&tmpu, "@");
+- rand_str = tmpu;
+- if (!source_user || !target_user || !rand_str) {
+- retval = -EINVAL;
+- goto out;
+- }
+-
+- /* hash the string user1@user2 with rand_str as the key */
+- len = strlen(source_user) + strlen(target_user) + 1;
+- /* src, @, len, \0 */
+- hash_str = kzalloc(len+1, GFP_KERNEL);
+- strcat(hash_str, source_user);
+- strcat(hash_str, "@");
+- strcat(hash_str, target_user);
+-
+- printk(KERN_ALERT "the source user is %s \n", source_user);
+- printk(KERN_ALERT "the target user is %s \n", target_user);
+-
+- result = cap_hash(hash_str, len, rand_str, strlen(rand_str));
+- if (NULL == result) {
+- retval = -EFAULT;
+- goto out;
+- }
+- memcpy(node_ptr->data, result, CAP_NODE_SIZE); /* why? */
+- /* Change the process's uid if the hash is present in the
+- * list of hashes
+- */
+- list_for_each(pos, &(cap_devices->head->list)) {
+- /*
+- * Change the user id of the process if the hashes
+- * match
+- */
+- if (0 ==
+- memcmp(result,
+- list_entry(pos, struct cap_node,
+- list)->data,
+- CAP_NODE_SIZE)) {
+- target_int = (unsigned int)
+- simple_strtol(target_user, NULL, 0);
+- source_int = (unsigned int)
+- simple_strtol(source_user, NULL, 0);
+- flag = 1;
+-
+- /*
+- * Check whether the process writing to capuse
+- * is actually owned by the source owner
+- */
+- if (source_int != current_uid()) {
+- printk(KERN_ALERT
+- "Process is not owned by the source user of the capability.\n");
+- retval = -EFAULT;
+- goto out;
+- }
+- /*
+- * What all id's need to be changed here? uid,
+- * euid, fsid, savedids ?? Currently I am
+- * changing the effective user id since most of
+- * the authorisation decisions are based on it
+- */
+- new = prepare_creds();
+- if (!new) {
+- retval = -ENOMEM;
+- goto out;
+- }
+- new->uid = (uid_t) target_int;
+- new->euid = (uid_t) target_int;
+- retval = commit_creds(new);
+- if (retval)
+- goto out;
+-
+- /*
+- * Remove the capability from the list and
+- * break
+- */
+- tmp = list_entry(pos, struct cap_node, list);
+- list_del(pos);
+- kfree(tmp);
+- break;
+- }
+- }
+- if (0 == flag) {
+- /*
+- * The capability is not present in the list of the
+- * hashes stored, hence return failure
+- */
+- printk(KERN_ALERT
+- "Invalid capabiliy written to /dev/capuse \n");
+- retval = -EFAULT;
+- goto out;
+- }
+- }
+- *f_pos += count;
+- retval = count;
+- /* update the size */
+- if (dev->size < *f_pos)
+- dev->size = *f_pos;
+-
+-out:
+- kfree(node_ptr);
+- kfree(user_buf);
+- kfree(user_buf_running);
+- kfree(hash_str);
+- up(&dev->sem);
+- return retval;
+-}
+-
+-static const struct file_operations cap_fops = {
+- .owner = THIS_MODULE,
+- .write = cap_write,
+- .open = cap_open,
+- .release = cap_release,
+-};
+-
+-/* no __exit here because it can be called by the init function */
+-static void cap_cleanup_module(void)
+-{
+- int i;
+- dev_t devno = MKDEV(cap_major, cap_minor);
+- if (cap_devices) {
+- for (i = 0; i < cap_nr_devs; i++) {
+- cap_trim(cap_devices + i);
+- cdev_del(&cap_devices[i].cdev);
+- }
+- kfree(cap_devices);
+- }
+- unregister_chrdev_region(devno, cap_nr_devs);
+-
+-}
+-
+-static void cap_setup_cdev(struct cap_dev *dev, int index)
+-{
+- int err, devno = MKDEV(cap_major, cap_minor + index);
+- cdev_init(&dev->cdev, &cap_fops);
+- dev->cdev.owner = THIS_MODULE;
+- dev->cdev.ops = &cap_fops;
+- err = cdev_add(&dev->cdev, devno, 1);
+- if (err)
+- printk(KERN_NOTICE "Error %d adding cap%d", err, index);
+-}
+-
+-static int __init cap_init_module(void)
+-{
+- int result, i;
+- dev_t dev = 0;
+-
+- if (cap_major) {
+- dev = MKDEV(cap_major, cap_minor);
+- result = register_chrdev_region(dev, cap_nr_devs, "cap");
+- } else {
+- result = alloc_chrdev_region(&dev, cap_minor, cap_nr_devs,
+- "cap");
+- cap_major = MAJOR(dev);
+- }
+-
+- if (result < 0) {
+- printk(KERN_WARNING "cap: can't get major %d\n",
+- cap_major);
+- return result;
+- }
+-
+- cap_devices = kzalloc(cap_nr_devs * sizeof(struct cap_dev),
+- GFP_KERNEL);
+- if (!cap_devices) {
+- result = -ENOMEM;
+- goto fail;
+- }
+-
+- /* Initialize each device. */
+- for (i = 0; i < cap_nr_devs; i++) {
+- cap_devices[i].node_size = cap_node_size;
+- init_MUTEX(&cap_devices[i].sem);
+- cap_setup_cdev(&cap_devices[i], i);
+- }
+-
+- return 0;
+-
+-fail:
+- cap_cleanup_module();
+- return result;
+-}
+-
+-module_init(cap_init_module);
+-module_exit(cap_cleanup_module);
+-
+-
diff --git a/staging/staging-remove-the-b3dfg-driver.patch b/staging/staging-remove-the-b3dfg-driver.patch
new file mode 100644
index 00000000000000..f92607454532b8
--- /dev/null
+++ b/staging/staging-remove-the-b3dfg-driver.patch
@@ -0,0 +1,1172 @@
+From foo@baz Tue Dec 22 16:29:26 PST 2009
+Date: Tue, 22 Dec 2009 16:29:26 -0800
+To: Greg KH <greg@kroah.com>
+From: Greg Kroah-Hartman <gregkh@suse.de>
+Subject: staging: remove the b3dfg driver
+
+It has no users, and no developers to maintain it to get
+it merged into mainline.
+
+So sad.
+
+Cc: Daniel Drake <ddrake@brontes3d.com>
+Cc: Justin Bronder <jsbronder@brontes3d.com>
+Cc: Duane Griffin <duaneg@dghda.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/staging/Kconfig | 2
+ drivers/staging/Makefile | 1
+ drivers/staging/b3dfg/Kconfig | 10
+ drivers/staging/b3dfg/Makefile | 1
+ drivers/staging/b3dfg/TODO | 4
+ drivers/staging/b3dfg/b3dfg.c | 1100 -----------------------------------------
+ 6 files changed, 1118 deletions(-)
+
+--- a/drivers/staging/b3dfg/b3dfg.c
++++ /dev/null
+@@ -1,1100 +0,0 @@
+- /*
+- * Brontes PCI frame grabber driver
+- *
+- * Copyright (C) 2008 3M Company
+- * Contact: Justin Bronder <jsbronder@brontes3d.com>
+- * Original Authors: Daniel Drake <ddrake@brontes3d.com>
+- * Duane Griffin <duaneg@dghda.com>
+- *
+- * 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
+- */
+-
+-#include <linux/device.h>
+-#include <linux/fs.h>
+-#include <linux/interrupt.h>
+-#include <linux/spinlock.h>
+-#include <linux/ioctl.h>
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/pci.h>
+-#include <linux/types.h>
+-#include <linux/cdev.h>
+-#include <linux/list.h>
+-#include <linux/poll.h>
+-#include <linux/wait.h>
+-#include <linux/mm.h>
+-#include <linux/uaccess.h>
+-#include <linux/sched.h>
+-
+-static unsigned int b3dfg_nbuf = 2;
+-
+-module_param_named(buffer_count, b3dfg_nbuf, uint, 0444);
+-
+-MODULE_PARM_DESC(buffer_count, "Number of buffers (min 2, default 2)");
+-
+-MODULE_AUTHOR("Daniel Drake <ddrake@brontes3d.com>");
+-MODULE_DESCRIPTION("Brontes frame grabber driver");
+-MODULE_LICENSE("GPL");
+-
+-#define DRIVER_NAME "b3dfg"
+-#define B3DFG_MAX_DEVS 4
+-#define B3DFG_FRAMES_PER_BUFFER 3
+-
+-#define B3DFG_BAR_REGS 0
+-#define B3DFG_REGS_LENGTH 0x10000
+-
+-#define B3DFG_IOC_MAGIC 0xb3 /* dfg :-) */
+-#define B3DFG_IOCGFRMSZ _IOR(B3DFG_IOC_MAGIC, 1, int)
+-#define B3DFG_IOCTNUMBUFS _IO(B3DFG_IOC_MAGIC, 2)
+-#define B3DFG_IOCTTRANS _IO(B3DFG_IOC_MAGIC, 3)
+-#define B3DFG_IOCTQUEUEBUF _IO(B3DFG_IOC_MAGIC, 4)
+-#define B3DFG_IOCTPOLLBUF _IOWR(B3DFG_IOC_MAGIC, 5, struct b3dfg_poll)
+-#define B3DFG_IOCTWAITBUF _IOWR(B3DFG_IOC_MAGIC, 6, struct b3dfg_wait)
+-#define B3DFG_IOCGWANDSTAT _IOR(B3DFG_IOC_MAGIC, 7, int)
+-
+-enum {
+- /* number of 4kb pages per frame */
+- B3D_REG_FRM_SIZE = 0x0,
+-
+- /* bit 0: set to enable interrupts
+- * bit 1: set to enable cable status change interrupts */
+- B3D_REG_HW_CTRL = 0x4,
+-
+- /* bit 0-1 - 1-based ID of next pending frame transfer (0 = none)
+- * bit 2 indicates the previous DMA transfer has completed
+- * bit 3 indicates wand cable status change
+- * bit 8:15 - counter of number of discarded triplets */
+- B3D_REG_DMA_STS = 0x8,
+-
+- /* bit 0: wand status (1 = present, 0 = disconnected) */
+- B3D_REG_WAND_STS = 0xc,
+-
+- /* bus address for DMA transfers. lower 2 bits must be zero because DMA
+- * works with 32 bit word size. */
+- B3D_REG_EC220_DMA_ADDR = 0x8000,
+-
+- /* bit 20:0 - number of 32 bit words to be transferred
+- * bit 21:31 - reserved */
+- B3D_REG_EC220_TRF_SIZE = 0x8004,
+-
+- /* bit 0 - error bit
+- * bit 1 - interrupt bit (set to generate interrupt at end of transfer)
+- * bit 2 - start bit (set to start transfer)
+- * bit 3 - direction (0 = DMA_TO_DEVICE, 1 = DMA_FROM_DEVICE
+- * bit 4:31 - reserved */
+- B3D_REG_EC220_DMA_STS = 0x8008,
+-};
+-
+-enum b3dfg_buffer_state {
+- B3DFG_BUFFER_POLLED = 0,
+- B3DFG_BUFFER_PENDING,
+- B3DFG_BUFFER_POPULATED,
+-};
+-
+-struct b3dfg_buffer {
+- unsigned char *frame[B3DFG_FRAMES_PER_BUFFER];
+- struct list_head list;
+- u8 state;
+-};
+-
+-struct b3dfg_dev {
+-
+- /* no protection needed: all finalized at initialization time */
+- struct pci_dev *pdev;
+- struct cdev chardev;
+- struct device *dev;
+- void __iomem *regs;
+- unsigned int frame_size;
+-
+- /*
+- * Protects buffer state, including buffer_queue, triplet_ready,
+- * cur_dma_frame_idx & cur_dma_frame_addr.
+- */
+- spinlock_t buffer_lock;
+- struct b3dfg_buffer *buffers;
+- struct list_head buffer_queue;
+-
+- /* Last frame in triplet transferred (-1 if none). */
+- int cur_dma_frame_idx;
+-
+- /* Current frame's address for DMA. */
+- dma_addr_t cur_dma_frame_addr;
+-
+- /*
+- * Protects cstate_tstamp.
+- * Nests inside buffer_lock.
+- */
+- spinlock_t cstate_lock;
+- unsigned long cstate_tstamp;
+-
+- /*
+- * Protects triplets_dropped.
+- * Nests inside buffers_lock.
+- */
+- spinlock_t triplets_dropped_lock;
+- unsigned int triplets_dropped;
+-
+- wait_queue_head_t buffer_waitqueue;
+-
+- unsigned int transmission_enabled:1;
+- unsigned int triplet_ready:1;
+-};
+-
+-static u8 b3dfg_devices[B3DFG_MAX_DEVS];
+-
+-static struct class *b3dfg_class;
+-static dev_t b3dfg_devt;
+-
+-static const struct pci_device_id b3dfg_ids[] __devinitdata = {
+- { PCI_DEVICE(0x0b3d, 0x0001) },
+- { },
+-};
+-
+-MODULE_DEVICE_TABLE(pci, b3dfg_ids);
+-
+-/***** user-visible types *****/
+-
+-struct b3dfg_poll {
+- int buffer_idx;
+- unsigned int triplets_dropped;
+-};
+-
+-struct b3dfg_wait {
+- int buffer_idx;
+- unsigned int timeout;
+- unsigned int triplets_dropped;
+-};
+-
+-/**** register I/O ****/
+-
+-static u32 b3dfg_read32(struct b3dfg_dev *fgdev, u16 reg)
+-{
+- return ioread32(fgdev->regs + reg);
+-}
+-
+-static void b3dfg_write32(struct b3dfg_dev *fgdev, u16 reg, u32 value)
+-{
+- iowrite32(value, fgdev->regs + reg);
+-}
+-
+-/**** buffer management ****/
+-
+-/*
+- * Program EC220 for transfer of a specific frame.
+- * Called with buffer_lock held.
+- */
+-static int setup_frame_transfer(struct b3dfg_dev *fgdev,
+- struct b3dfg_buffer *buf, int frame)
+-{
+- unsigned char *frm_addr;
+- dma_addr_t frm_addr_dma;
+- unsigned int frm_size = fgdev->frame_size;
+-
+- frm_addr = buf->frame[frame];
+- frm_addr_dma = pci_map_single(fgdev->pdev, frm_addr,
+- frm_size, PCI_DMA_FROMDEVICE);
+- if (pci_dma_mapping_error(fgdev->pdev, frm_addr_dma))
+- return -ENOMEM;
+-
+- fgdev->cur_dma_frame_addr = frm_addr_dma;
+- fgdev->cur_dma_frame_idx = frame;
+-
+- b3dfg_write32(fgdev, B3D_REG_EC220_DMA_ADDR,
+- cpu_to_le32(frm_addr_dma));
+- b3dfg_write32(fgdev, B3D_REG_EC220_TRF_SIZE,
+- cpu_to_le32(frm_size >> 2));
+- b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0xf);
+-
+- return 0;
+-}
+-
+-/* Caller should hold buffer lock */
+-static void dequeue_all_buffers(struct b3dfg_dev *fgdev)
+-{
+- int i;
+- for (i = 0; i < b3dfg_nbuf; i++) {
+- struct b3dfg_buffer *buf = &fgdev->buffers[i];
+- buf->state = B3DFG_BUFFER_POLLED;
+- list_del_init(&buf->list);
+- }
+-}
+-
+-/* queue a buffer to receive data */
+-static int queue_buffer(struct b3dfg_dev *fgdev, int bufidx)
+-{
+- struct device *dev = &fgdev->pdev->dev;
+- struct b3dfg_buffer *buf;
+- unsigned long flags;
+- int r = 0;
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+- if (bufidx < 0 || bufidx >= b3dfg_nbuf) {
+- dev_dbg(dev, "Invalid buffer index, %d\n", bufidx);
+- r = -ENOENT;
+- goto out;
+- }
+- buf = &fgdev->buffers[bufidx];
+-
+- if (unlikely(buf->state == B3DFG_BUFFER_PENDING)) {
+- dev_dbg(dev, "buffer %d is already queued\n", bufidx);
+- r = -EINVAL;
+- goto out;
+- }
+-
+- buf->state = B3DFG_BUFFER_PENDING;
+- list_add_tail(&buf->list, &fgdev->buffer_queue);
+-
+- if (fgdev->transmission_enabled && fgdev->triplet_ready) {
+- dev_dbg(dev, "triplet is ready, pushing immediately\n");
+- fgdev->triplet_ready = 0;
+- r = setup_frame_transfer(fgdev, buf, 0);
+- if (r)
+- dev_err(dev, "unable to map DMA buffer\n");
+- }
+-
+-out:
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+- return r;
+-}
+-
+-/* non-blocking buffer poll. returns 1 if data is present in the buffer,
+- * 0 otherwise */
+-static int poll_buffer(struct b3dfg_dev *fgdev, void __user *arg)
+-{
+- struct device *dev = &fgdev->pdev->dev;
+- struct b3dfg_poll p;
+- struct b3dfg_buffer *buf;
+- unsigned long flags;
+- int r = 1;
+- int arg_out = 0;
+-
+- if (copy_from_user(&p, arg, sizeof(p)))
+- return -EFAULT;
+-
+- if (unlikely(!fgdev->transmission_enabled)) {
+- dev_dbg(dev, "cannot poll, transmission disabled\n");
+- return -EINVAL;
+- }
+-
+- if (p.buffer_idx < 0 || p.buffer_idx >= b3dfg_nbuf)
+- return -ENOENT;
+-
+- buf = &fgdev->buffers[p.buffer_idx];
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+-
+- if (likely(buf->state == B3DFG_BUFFER_POPULATED)) {
+- arg_out = 1;
+- buf->state = B3DFG_BUFFER_POLLED;
+-
+- /* IRQs already disabled by spin_lock_irqsave above. */
+- spin_lock(&fgdev->triplets_dropped_lock);
+- p.triplets_dropped = fgdev->triplets_dropped;
+- fgdev->triplets_dropped = 0;
+- spin_unlock(&fgdev->triplets_dropped_lock);
+- } else {
+- r = 0;
+- }
+-
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- if (arg_out && copy_to_user(arg, &p, sizeof(p)))
+- r = -EFAULT;
+-
+- return r;
+-}
+-
+-static unsigned long get_cstate_change(struct b3dfg_dev *fgdev)
+-{
+- unsigned long flags, when;
+-
+- spin_lock_irqsave(&fgdev->cstate_lock, flags);
+- when = fgdev->cstate_tstamp;
+- spin_unlock_irqrestore(&fgdev->cstate_lock, flags);
+- return when;
+-}
+-
+-static int is_event_ready(struct b3dfg_dev *fgdev, struct b3dfg_buffer *buf,
+- unsigned long when)
+-{
+- int result;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+- spin_lock(&fgdev->cstate_lock);
+- result = (!fgdev->transmission_enabled ||
+- buf->state == B3DFG_BUFFER_POPULATED ||
+- when != fgdev->cstate_tstamp);
+- spin_unlock(&fgdev->cstate_lock);
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- return result;
+-}
+-
+-/* sleep until a specific buffer becomes populated */
+-static int wait_buffer(struct b3dfg_dev *fgdev, void __user *arg)
+-{
+- struct device *dev = &fgdev->pdev->dev;
+- struct b3dfg_wait w;
+- struct b3dfg_buffer *buf;
+- unsigned long flags, when;
+- int r;
+-
+- if (copy_from_user(&w, arg, sizeof(w)))
+- return -EFAULT;
+-
+- if (!fgdev->transmission_enabled) {
+- dev_dbg(dev, "cannot wait, transmission disabled\n");
+- return -EINVAL;
+- }
+-
+- if (w.buffer_idx < 0 || w.buffer_idx >= b3dfg_nbuf)
+- return -ENOENT;
+-
+- buf = &fgdev->buffers[w.buffer_idx];
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+-
+- if (buf->state == B3DFG_BUFFER_POPULATED) {
+- r = w.timeout;
+- goto out_triplets_dropped;
+- }
+-
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- when = get_cstate_change(fgdev);
+- if (w.timeout > 0) {
+- r = wait_event_interruptible_timeout(fgdev->buffer_waitqueue,
+- is_event_ready(fgdev, buf, when),
+- (w.timeout * HZ) / 1000);
+-
+- if (unlikely(r < 0))
+- goto out;
+-
+- w.timeout = r * 1000 / HZ;
+- } else {
+- r = wait_event_interruptible(fgdev->buffer_waitqueue,
+- is_event_ready(fgdev, buf, when));
+-
+- if (unlikely(r)) {
+- r = -ERESTARTSYS;
+- goto out;
+- }
+- }
+-
+- /* TODO: Inform the user via field(s) in w? */
+- if (!fgdev->transmission_enabled || when != get_cstate_change(fgdev)) {
+- r = -EINVAL;
+- goto out;
+- }
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+-
+- if (buf->state != B3DFG_BUFFER_POPULATED) {
+- r = -ETIMEDOUT;
+- goto out_unlock;
+- }
+-
+- buf->state = B3DFG_BUFFER_POLLED;
+-
+-out_triplets_dropped:
+-
+- /* IRQs already disabled by spin_lock_irqsave above. */
+- spin_lock(&fgdev->triplets_dropped_lock);
+- w.triplets_dropped = fgdev->triplets_dropped;
+- fgdev->triplets_dropped = 0;
+- spin_unlock(&fgdev->triplets_dropped_lock);
+-
+-out_unlock:
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+- if (copy_to_user(arg, &w, sizeof(w)))
+- r = -EFAULT;
+-out:
+- return r;
+-}
+-
+-/* mmap page fault handler */
+-static int b3dfg_vma_fault(struct vm_area_struct *vma,
+- struct vm_fault *vmf)
+-{
+- struct b3dfg_dev *fgdev = vma->vm_file->private_data;
+- unsigned long off = vmf->pgoff << PAGE_SHIFT;
+- unsigned int frame_size = fgdev->frame_size;
+- unsigned int buf_size = frame_size * B3DFG_FRAMES_PER_BUFFER;
+- unsigned char *addr;
+-
+- /* determine which buffer the offset lies within */
+- unsigned int buf_idx = off / buf_size;
+- /* and the offset into the buffer */
+- unsigned int buf_off = off % buf_size;
+-
+- /* determine which frame inside the buffer the offset lies in */
+- unsigned int frm_idx = buf_off / frame_size;
+- /* and the offset into the frame */
+- unsigned int frm_off = buf_off % frame_size;
+-
+- if (unlikely(buf_idx >= b3dfg_nbuf))
+- return VM_FAULT_SIGBUS;
+-
+- addr = fgdev->buffers[buf_idx].frame[frm_idx] + frm_off;
+- vm_insert_pfn(vma, (unsigned long)vmf->virtual_address,
+- virt_to_phys(addr) >> PAGE_SHIFT);
+-
+- return VM_FAULT_NOPAGE;
+-}
+-
+-static struct vm_operations_struct b3dfg_vm_ops = {
+- .fault = b3dfg_vma_fault,
+-};
+-
+-static int get_wand_status(struct b3dfg_dev *fgdev, int __user *arg)
+-{
+- u32 wndstat = b3dfg_read32(fgdev, B3D_REG_WAND_STS);
+- dev_dbg(&fgdev->pdev->dev, "wand status %x\n", wndstat);
+- return __put_user(wndstat & 0x1, arg);
+-}
+-
+-static int enable_transmission(struct b3dfg_dev *fgdev)
+-{
+- unsigned long flags;
+- struct device *dev = &fgdev->pdev->dev;
+-
+- dev_dbg(dev, "enable transmission\n");
+-
+- /* check the cable is plugged in. */
+- if (!b3dfg_read32(fgdev, B3D_REG_WAND_STS)) {
+- dev_dbg(dev, "cannot start transmission without wand\n");
+- return -EINVAL;
+- }
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+-
+- /* Handle racing enable_transmission calls. */
+- if (fgdev->transmission_enabled) {
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+- goto out;
+- }
+-
+- spin_lock(&fgdev->triplets_dropped_lock);
+- fgdev->triplets_dropped = 0;
+- spin_unlock(&fgdev->triplets_dropped_lock);
+-
+- fgdev->triplet_ready = 0;
+- fgdev->cur_dma_frame_idx = -1;
+- fgdev->transmission_enabled = 1;
+-
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- /* Enable DMA and cable status interrupts. */
+- b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0x03);
+-
+-out:
+- return 0;
+-}
+-
+-static void disable_transmission(struct b3dfg_dev *fgdev)
+-{
+- struct device *dev = &fgdev->pdev->dev;
+- unsigned long flags;
+- u32 tmp;
+-
+- dev_dbg(dev, "disable transmission\n");
+-
+- /* guarantee that no more interrupts will be serviced */
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+- fgdev->transmission_enabled = 0;
+-
+- b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0);
+-
+- /* FIXME: temporary debugging only. if the board stops transmitting,
+- * hitting ctrl+c and seeing this message is useful for determining
+- * the state of the board. */
+- tmp = b3dfg_read32(fgdev, B3D_REG_DMA_STS);
+- dev_dbg(dev, "DMA_STS reads %x after TX stopped\n", tmp);
+-
+- dequeue_all_buffers(fgdev);
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- wake_up_interruptible(&fgdev->buffer_waitqueue);
+-}
+-
+-static int set_transmission(struct b3dfg_dev *fgdev, int enabled)
+-{
+- int res = 0;
+-
+- if (enabled && !fgdev->transmission_enabled)
+- res = enable_transmission(fgdev);
+- else if (!enabled && fgdev->transmission_enabled)
+- disable_transmission(fgdev);
+-
+- return res;
+-}
+-
+-/* Called in interrupt context. */
+-static void handle_cstate_unplug(struct b3dfg_dev *fgdev)
+-{
+- /* Disable all interrupts. */
+- b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0);
+-
+- /* Stop transmission. */
+- spin_lock(&fgdev->buffer_lock);
+- fgdev->transmission_enabled = 0;
+-
+- fgdev->cur_dma_frame_idx = -1;
+- fgdev->triplet_ready = 0;
+- if (fgdev->cur_dma_frame_addr) {
+- pci_unmap_single(fgdev->pdev, fgdev->cur_dma_frame_addr,
+- fgdev->frame_size, PCI_DMA_FROMDEVICE);
+- fgdev->cur_dma_frame_addr = 0;
+- }
+- dequeue_all_buffers(fgdev);
+- spin_unlock(&fgdev->buffer_lock);
+-}
+-
+-/* Called in interrupt context. */
+-static void handle_cstate_change(struct b3dfg_dev *fgdev)
+-{
+- u32 cstate = b3dfg_read32(fgdev, B3D_REG_WAND_STS);
+- unsigned long when;
+- struct device *dev = &fgdev->pdev->dev;
+-
+- dev_dbg(dev, "cable state change: %u\n", cstate);
+-
+- /*
+- * When the wand is unplugged we reset our state. The hardware will
+- * have done the same internally.
+- *
+- * Note we should never see a cable *plugged* event, as interrupts
+- * should only be enabled when transmitting, which requires the cable
+- * to be plugged. If we do see one it probably means the cable has been
+- * unplugged and re-plugged very rapidly. Possibly because it has a
+- * broken wire and is momentarily losing contact.
+- *
+- * TODO: At the moment if you plug in the cable then enable transmission
+- * the hardware will raise a couple of spurious interrupts, so
+- * just ignore them for now.
+- *
+- * Once the hardware is fixed we should complain and treat it as an
+- * unplug. Or at least track how frequently it is happening and do
+- * so if too many come in.
+- */
+- if (cstate) {
+- dev_warn(dev, "ignoring unexpected plug event\n");
+- return;
+- }
+- handle_cstate_unplug(fgdev);
+-
+- /*
+- * Record cable state change timestamp & wake anyone waiting
+- * on a cable state change. Be paranoid about ensuring events
+- * are not missed if we somehow get two interrupts in a jiffy.
+- */
+- spin_lock(&fgdev->cstate_lock);
+- when = jiffies_64;
+- if (when <= fgdev->cstate_tstamp)
+- when = fgdev->cstate_tstamp + 1;
+- fgdev->cstate_tstamp = when;
+- wake_up_interruptible(&fgdev->buffer_waitqueue);
+- spin_unlock(&fgdev->cstate_lock);
+-}
+-
+-/* Called with buffer_lock held. */
+-static void transfer_complete(struct b3dfg_dev *fgdev)
+-{
+- struct b3dfg_buffer *buf;
+- struct device *dev = &fgdev->pdev->dev;
+-
+- pci_unmap_single(fgdev->pdev, fgdev->cur_dma_frame_addr,
+- fgdev->frame_size, PCI_DMA_FROMDEVICE);
+- fgdev->cur_dma_frame_addr = 0;
+-
+- buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list);
+-
+- dev_dbg(dev, "handle frame completion\n");
+- if (fgdev->cur_dma_frame_idx == B3DFG_FRAMES_PER_BUFFER - 1) {
+-
+- /* last frame of that triplet completed */
+- dev_dbg(dev, "triplet completed\n");
+- buf->state = B3DFG_BUFFER_POPULATED;
+- list_del_init(&buf->list);
+- wake_up_interruptible(&fgdev->buffer_waitqueue);
+- }
+-}
+-
+-/*
+- * Called with buffer_lock held.
+- *
+- * Note that idx is the (1-based) *next* frame to be transferred, while
+- * cur_dma_frame_idx is the (0-based) *last* frame to have been transferred (or
+- * -1 if none). Thus there should be a difference of 2 between them.
+- */
+-static bool setup_next_frame_transfer(struct b3dfg_dev *fgdev, int idx)
+-{
+- struct b3dfg_buffer *buf;
+- struct device *dev = &fgdev->pdev->dev;
+- bool need_ack = 1;
+-
+- dev_dbg(dev, "program DMA transfer for next frame: %d\n", idx);
+-
+- buf = list_entry(fgdev->buffer_queue.next, struct b3dfg_buffer, list);
+- if (idx == fgdev->cur_dma_frame_idx + 2) {
+- if (setup_frame_transfer(fgdev, buf, idx - 1))
+- dev_err(dev, "unable to map DMA buffer\n");
+- need_ack = 0;
+- } else {
+- dev_err(dev, "frame mismatch, got %d, expected %d\n",
+- idx, fgdev->cur_dma_frame_idx + 2);
+-
+- /* FIXME: handle dropped triplets here */
+- }
+-
+- return need_ack;
+-}
+-
+-static irqreturn_t b3dfg_intr(int irq, void *dev_id)
+-{
+- struct b3dfg_dev *fgdev = dev_id;
+- struct device *dev = &fgdev->pdev->dev;
+- u32 sts;
+- u8 dropped;
+- bool need_ack = 1;
+- irqreturn_t res = IRQ_HANDLED;
+-
+- sts = b3dfg_read32(fgdev, B3D_REG_DMA_STS);
+- if (unlikely(sts == 0)) {
+- dev_warn(dev, "ignore interrupt, DMA status is 0\n");
+- res = IRQ_NONE;
+- goto out;
+- }
+-
+- if (unlikely(!fgdev->transmission_enabled)) {
+- dev_warn(dev, "ignore interrupt, TX disabled\n");
+- res = IRQ_HANDLED;
+- goto out;
+- }
+-
+- /* Handle dropped frames, as reported by the hardware. */
+- dropped = (sts >> 8) & 0xff;
+- dev_dbg(dev, "intr: DMA_STS=%08x (drop=%d comp=%d next=%d)\n",
+- sts, dropped, !!(sts & 0x4), sts & 0x3);
+- if (unlikely(dropped > 0)) {
+- spin_lock(&fgdev->triplets_dropped_lock);
+- fgdev->triplets_dropped += dropped;
+- spin_unlock(&fgdev->triplets_dropped_lock);
+- }
+-
+- /* Handle a cable state change (i.e. the wand being unplugged). */
+- if (sts & 0x08) {
+- handle_cstate_change(fgdev);
+- goto out;
+- }
+-
+- spin_lock(&fgdev->buffer_lock);
+- if (unlikely(list_empty(&fgdev->buffer_queue))) {
+-
+- /* FIXME need more sanity checking here */
+- dev_info(dev, "buffer not ready for next transfer\n");
+- fgdev->triplet_ready = 1;
+- goto out_unlock;
+- }
+-
+- /* Has a frame transfer been completed? */
+- if (sts & 0x4) {
+- u32 dma_status = b3dfg_read32(fgdev, B3D_REG_EC220_DMA_STS);
+-
+- /* Check for DMA errors reported by the hardware. */
+- if (unlikely(dma_status & 0x1)) {
+- dev_err(dev, "EC220 error: %08x\n", dma_status);
+-
+- /* FIXME flesh out error handling */
+- goto out_unlock;
+- }
+-
+- /* Sanity check, we should have a frame index at this point. */
+- if (unlikely(fgdev->cur_dma_frame_idx == -1)) {
+- dev_err(dev, "completed but no last idx?\n");
+-
+- /* FIXME flesh out error handling */
+- goto out_unlock;
+- }
+-
+- transfer_complete(fgdev);
+- }
+-
+- /* Is there another frame transfer pending? */
+- if (sts & 0x3)
+- need_ack = setup_next_frame_transfer(fgdev, sts & 0x3);
+- else
+- fgdev->cur_dma_frame_idx = -1;
+-
+-out_unlock:
+- spin_unlock(&fgdev->buffer_lock);
+-out:
+- if (need_ack) {
+- dev_dbg(dev, "acknowledging interrupt\n");
+- b3dfg_write32(fgdev, B3D_REG_EC220_DMA_STS, 0x0b);
+- }
+- return res;
+-}
+-
+-static int b3dfg_open(struct inode *inode, struct file *filp)
+-{
+- struct b3dfg_dev *fgdev =
+- container_of(inode->i_cdev, struct b3dfg_dev, chardev);
+-
+- dev_dbg(&fgdev->pdev->dev, "open\n");
+- filp->private_data = fgdev;
+- return 0;
+-}
+-
+-static int b3dfg_release(struct inode *inode, struct file *filp)
+-{
+- struct b3dfg_dev *fgdev = filp->private_data;
+- dev_dbg(&fgdev->pdev->dev, "release\n");
+- disable_transmission(fgdev);
+- return 0;
+-}
+-
+-static long b3dfg_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+-{
+- struct b3dfg_dev *fgdev = filp->private_data;
+-
+- switch (cmd) {
+- case B3DFG_IOCGFRMSZ:
+- return __put_user(fgdev->frame_size, (int __user *) arg);
+- case B3DFG_IOCGWANDSTAT:
+- return get_wand_status(fgdev, (int __user *) arg);
+- case B3DFG_IOCTTRANS:
+- return set_transmission(fgdev, (int) arg);
+- case B3DFG_IOCTQUEUEBUF:
+- return queue_buffer(fgdev, (int) arg);
+- case B3DFG_IOCTPOLLBUF:
+- return poll_buffer(fgdev, (void __user *) arg);
+- case B3DFG_IOCTWAITBUF:
+- return wait_buffer(fgdev, (void __user *) arg);
+- default:
+- dev_dbg(&fgdev->pdev->dev, "unrecognised ioctl %x\n", cmd);
+- return -EINVAL;
+- }
+-}
+-
+-static unsigned int b3dfg_poll(struct file *filp, poll_table *poll_table)
+-{
+- struct b3dfg_dev *fgdev = filp->private_data;
+- unsigned long flags, when;
+- int i;
+- int r = 0;
+-
+- when = get_cstate_change(fgdev);
+- poll_wait(filp, &fgdev->buffer_waitqueue, poll_table);
+-
+- spin_lock_irqsave(&fgdev->buffer_lock, flags);
+- for (i = 0; i < b3dfg_nbuf; i++) {
+- if (fgdev->buffers[i].state == B3DFG_BUFFER_POPULATED) {
+- r = POLLIN | POLLRDNORM;
+- break;
+- }
+- }
+- spin_unlock_irqrestore(&fgdev->buffer_lock, flags);
+-
+- /* TODO: Confirm this is how we want to communicate the change. */
+- if (!fgdev->transmission_enabled || when != get_cstate_change(fgdev))
+- r = POLLERR;
+-
+- return r;
+-}
+-
+-static int b3dfg_mmap(struct file *filp, struct vm_area_struct *vma)
+-{
+- struct b3dfg_dev *fgdev = filp->private_data;
+- unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+- unsigned long vsize = vma->vm_end - vma->vm_start;
+- unsigned long bufdatalen = b3dfg_nbuf * fgdev->frame_size * 3;
+- unsigned long psize = bufdatalen - offset;
+- int r = 0;
+-
+- if (vsize <= psize) {
+- vma->vm_flags |= VM_IO | VM_RESERVED | VM_CAN_NONLINEAR |
+- VM_PFNMAP;
+- vma->vm_ops = &b3dfg_vm_ops;
+- } else {
+- r = -EINVAL;
+- }
+-
+- return r;
+-}
+-
+-static struct file_operations b3dfg_fops = {
+- .owner = THIS_MODULE,
+- .open = b3dfg_open,
+- .release = b3dfg_release,
+- .unlocked_ioctl = b3dfg_ioctl,
+- .poll = b3dfg_poll,
+- .mmap = b3dfg_mmap,
+-};
+-
+-static void free_all_frame_buffers(struct b3dfg_dev *fgdev)
+-{
+- int i, j;
+- for (i = 0; i < b3dfg_nbuf; i++)
+- for (j = 0; j < B3DFG_FRAMES_PER_BUFFER; j++)
+- kfree(fgdev->buffers[i].frame[j]);
+- kfree(fgdev->buffers);
+-}
+-
+-/* initialize device and any data structures. called before any interrupts
+- * are enabled. */
+-static int b3dfg_init_dev(struct b3dfg_dev *fgdev)
+-{
+- int i, j;
+- u32 frm_size = b3dfg_read32(fgdev, B3D_REG_FRM_SIZE);
+-
+- /* Disable interrupts. In abnormal circumstances (e.g. after a crash)
+- * the board may still be transmitting from the previous session. If we
+- * ensure that interrupts are disabled before we later enable them, we
+- * are sure to capture a triplet from the start, rather than starting
+- * from frame 2 or 3. Disabling interrupts causes the FG to throw away
+- * all buffered data and stop buffering more until interrupts are
+- * enabled again.
+- */
+- b3dfg_write32(fgdev, B3D_REG_HW_CTRL, 0);
+-
+- fgdev->frame_size = frm_size * 4096;
+- fgdev->buffers = kzalloc(sizeof(struct b3dfg_buffer) * b3dfg_nbuf,
+- GFP_KERNEL);
+- if (!fgdev->buffers)
+- goto err_no_buf;
+- for (i = 0; i < b3dfg_nbuf; i++) {
+- struct b3dfg_buffer *buf = &fgdev->buffers[i];
+- for (j = 0; j < B3DFG_FRAMES_PER_BUFFER; j++) {
+- buf->frame[j] = kmalloc(fgdev->frame_size, GFP_KERNEL);
+- if (!buf->frame[j])
+- goto err_no_mem;
+- }
+- INIT_LIST_HEAD(&buf->list);
+- }
+-
+- INIT_LIST_HEAD(&fgdev->buffer_queue);
+- init_waitqueue_head(&fgdev->buffer_waitqueue);
+- spin_lock_init(&fgdev->buffer_lock);
+- spin_lock_init(&fgdev->cstate_lock);
+- spin_lock_init(&fgdev->triplets_dropped_lock);
+- return 0;
+-
+-err_no_mem:
+- free_all_frame_buffers(fgdev);
+-err_no_buf:
+- return -ENOMEM;
+-}
+-
+-/* find next free minor number, returns -1 if none are availabile */
+-static int get_free_minor(void)
+-{
+- int i;
+- for (i = 0; i < B3DFG_MAX_DEVS; i++) {
+- if (b3dfg_devices[i] == 0)
+- return i;
+- }
+- return -1;
+-}
+-
+-static int __devinit b3dfg_probe(struct pci_dev *pdev,
+- const struct pci_device_id *id)
+-{
+- struct b3dfg_dev *fgdev = kzalloc(sizeof(*fgdev), GFP_KERNEL);
+- int r = 0;
+- int minor = get_free_minor();
+- dev_t devno = MKDEV(MAJOR(b3dfg_devt), minor);
+- unsigned long res_len;
+- resource_size_t res_base;
+-
+- if (fgdev == NULL)
+- return -ENOMEM;
+-
+- if (minor < 0) {
+- dev_err(&pdev->dev, "too many devices found!\n");
+- r = -EIO;
+- goto err_free;
+- }
+-
+- b3dfg_devices[minor] = 1;
+- dev_info(&pdev->dev, "probe device with IRQ %d\n", pdev->irq);
+-
+- cdev_init(&fgdev->chardev, &b3dfg_fops);
+- fgdev->chardev.owner = THIS_MODULE;
+-
+- r = cdev_add(&fgdev->chardev, devno, 1);
+- if (r) {
+- dev_err(&pdev->dev, "cannot add char device\n");
+- goto err_release_minor;
+- }
+-
+- fgdev->dev = device_create(
+- b3dfg_class,
+- &pdev->dev,
+- devno,
+- dev_get_drvdata(&pdev->dev),
+- DRIVER_NAME "%d", minor);
+-
+- if (IS_ERR(fgdev->dev)) {
+- dev_err(&pdev->dev, "cannot create device\n");
+- r = PTR_ERR(fgdev->dev);
+- goto err_del_cdev;
+- }
+-
+- r = pci_enable_device(pdev);
+- if (r) {
+- dev_err(&pdev->dev, "cannot enable PCI device\n");
+- goto err_dev_unreg;
+- }
+-
+- res_len = pci_resource_len(pdev, B3DFG_BAR_REGS);
+- if (res_len != B3DFG_REGS_LENGTH) {
+- dev_err(&pdev->dev, "invalid register resource size\n");
+- r = -EIO;
+- goto err_disable;
+- }
+-
+- if (pci_resource_flags(pdev, B3DFG_BAR_REGS)
+- != (IORESOURCE_MEM | IORESOURCE_SIZEALIGN)) {
+- dev_err(&pdev->dev, "invalid resource flags\n");
+- r = -EIO;
+- goto err_disable;
+- }
+- r = pci_request_regions(pdev, DRIVER_NAME);
+- if (r) {
+- dev_err(&pdev->dev, "cannot obtain PCI resources\n");
+- goto err_disable;
+- }
+-
+- pci_set_master(pdev);
+-
+- r = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+- if (r) {
+- dev_err(&pdev->dev, "no usable DMA configuration\n");
+- goto err_free_res;
+- }
+-
+- res_base = pci_resource_start(pdev, B3DFG_BAR_REGS);
+- fgdev->regs = ioremap_nocache(res_base, res_len);
+- if (!fgdev->regs) {
+- dev_err(&pdev->dev, "regs ioremap failed\n");
+- r = -EIO;
+- goto err_free_res;
+- }
+-
+- fgdev->pdev = pdev;
+- pci_set_drvdata(pdev, fgdev);
+- r = b3dfg_init_dev(fgdev);
+- if (r < 0) {
+- dev_err(&pdev->dev, "failed to initalize device\n");
+- goto err_unmap;
+- }
+-
+- r = request_irq(pdev->irq, b3dfg_intr, IRQF_SHARED, DRIVER_NAME, fgdev);
+- if (r) {
+- dev_err(&pdev->dev, "couldn't request irq %d\n", pdev->irq);
+- goto err_free_bufs;
+- }
+-
+- return 0;
+-
+-err_free_bufs:
+- free_all_frame_buffers(fgdev);
+-err_unmap:
+- iounmap(fgdev->regs);
+-err_free_res:
+- pci_release_regions(pdev);
+-err_disable:
+- pci_disable_device(pdev);
+-err_dev_unreg:
+- device_destroy(b3dfg_class, devno);
+-err_del_cdev:
+- cdev_del(&fgdev->chardev);
+-err_release_minor:
+- b3dfg_devices[minor] = 0;
+-err_free:
+- kfree(fgdev);
+- return r;
+-}
+-
+-static void __devexit b3dfg_remove(struct pci_dev *pdev)
+-{
+- struct b3dfg_dev *fgdev = pci_get_drvdata(pdev);
+- unsigned int minor = MINOR(fgdev->chardev.dev);
+-
+- dev_dbg(&pdev->dev, "remove\n");
+-
+- free_irq(pdev->irq, fgdev);
+- iounmap(fgdev->regs);
+- pci_release_regions(pdev);
+- pci_disable_device(pdev);
+- device_destroy(b3dfg_class, MKDEV(MAJOR(b3dfg_devt), minor));
+- cdev_del(&fgdev->chardev);
+- free_all_frame_buffers(fgdev);
+- kfree(fgdev);
+- b3dfg_devices[minor] = 0;
+-}
+-
+-static struct pci_driver b3dfg_driver = {
+- .name = DRIVER_NAME,
+- .id_table = b3dfg_ids,
+- .probe = b3dfg_probe,
+- .remove = __devexit_p(b3dfg_remove),
+-};
+-
+-static int __init b3dfg_module_init(void)
+-{
+- int r;
+-
+- if (b3dfg_nbuf < 2) {
+- printk(KERN_ERR DRIVER_NAME
+- ": buffer_count is out of range (must be >= 2)");
+- return -EINVAL;
+- }
+-
+- printk(KERN_INFO DRIVER_NAME ": loaded\n");
+-
+- b3dfg_class = class_create(THIS_MODULE, DRIVER_NAME);
+- if (IS_ERR(b3dfg_class))
+- return PTR_ERR(b3dfg_class);
+-
+- r = alloc_chrdev_region(&b3dfg_devt, 0, B3DFG_MAX_DEVS, DRIVER_NAME);
+- if (r)
+- goto err1;
+-
+- r = pci_register_driver(&b3dfg_driver);
+- if (r)
+- goto err2;
+-
+- return r;
+-
+-err2:
+- unregister_chrdev_region(b3dfg_devt, B3DFG_MAX_DEVS);
+-err1:
+- class_destroy(b3dfg_class);
+- return r;
+-}
+-
+-static void __exit b3dfg_module_exit(void)
+-{
+- printk(KERN_INFO DRIVER_NAME ": unloaded\n");
+- pci_unregister_driver(&b3dfg_driver);
+- unregister_chrdev_region(b3dfg_devt, B3DFG_MAX_DEVS);
+- class_destroy(b3dfg_class);
+-}
+-
+-module_init(b3dfg_module_init);
+-module_exit(b3dfg_module_exit);
+--- a/drivers/staging/b3dfg/Kconfig
++++ /dev/null
+@@ -1,10 +0,0 @@
+-config B3DFG
+- tristate "Brontes 3d Frame Framegrabber"
+- depends on PCI
+- default n
+- ---help---
+- This driver provides support for the Brontes 3d Framegrabber
+- PCI card.
+-
+- To compile this driver as a module, choose M here. The module
+- will be called b3dfg.
+--- a/drivers/staging/b3dfg/Makefile
++++ /dev/null
+@@ -1 +0,0 @@
+-obj-$(CONFIG_B3DFG) += b3dfg.o
+--- a/drivers/staging/b3dfg/TODO
++++ /dev/null
+@@ -1,4 +0,0 @@
+-
+- - queue/wait buffer presents filltime results for each frame?
+- - counting of dropped frames
+- - review endianness
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -89,8 +89,6 @@ source "drivers/staging/dream/Kconfig"
+
+ source "drivers/staging/pohmelfs/Kconfig"
+
+-source "drivers/staging/b3dfg/Kconfig"
+-
+ source "drivers/staging/phison/Kconfig"
+
+ source "drivers/staging/p9auth/Kconfig"
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -27,7 +27,6 @@ obj-$(CONFIG_INPUT_MIMIO) += mimio/
+ obj-$(CONFIG_TRANZPORT) += frontier/
+ obj-$(CONFIG_DREAM) += dream/
+ obj-$(CONFIG_POHMELFS) += pohmelfs/
+-obj-$(CONFIG_B3DFG) += b3dfg/
+ obj-$(CONFIG_IDE_PHISON) += phison/
+ obj-$(CONFIG_PLAN9AUTH) += p9auth/
+ obj-$(CONFIG_LINE6_USB) += line6/