diff options
| -rw-r--r-- | series | 4 | ||||
| -rw-r--r-- | staging/staging-altpciechdma-remove-driver.patch | 1263 | ||||
| -rw-r--r-- | staging/staging-p9auth-remove-driver-from-tree.patch | 467 | ||||
| -rw-r--r-- | staging/staging-remove-the-b3dfg-driver.patch | 1172 |
4 files changed, 2906 insertions, 0 deletions
@@ -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/ |
