Signed-off-by: Eli Billauer
---
drivers/uio/Kconfig | 32 +
drivers/uio/Makefile | 3 +
drivers/uio/xillybus.h | 185 ++++
drivers/uio/xillybus_core.c | 2345 +++++++++++++++++++++++++++++++++++++++++++
drivers/uio/xillybus_of.c | 210 ++++
drivers/uio/xillybus_pcie.c | 260 +++++
6 files changed, 3035 insertions(+), 0 deletions(-)
create mode 100644 drivers/uio/xillybus.h
create mode 100644 drivers/uio/xillybus_core.c
create mode 100644 drivers/uio/xillybus_of.c
create mode 100644 drivers/uio/xillybus_pcie.c
diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig
index e92eeaf..81ea8ab 100644
--- a/drivers/uio/Kconfig
+++ b/drivers/uio/Kconfig
@@ -127,4 +127,36 @@ config UIO_PRUSS
To compile this driver as a module, choose M here: the module
will be called uio_pruss.
+config XILLYBUS
+ tristate "Xillybus Support"
+ depends on PCI || (OF_ADDRESS && OF_DEVICE && OF_IRQ)
+ default n
+ help
+ Xillybus is a generic interface for peripherals designed on
+ programmable logic (FPGA). The driver probes the hardware for
+ its capabilities, and creates device files accordingly.
+
+ If unsure, say N.
+
+config XILLYBUS_PCIE
+ tristate "Xillybus over PCIe"
+ depends on XILLYBUS && PCI
+ default n
+ help
+ Set to M if you want Xillybus to use PCI Express for communicating
+ with the FPGA. This option is harmless, but it requires PCI
+ support on the kernel. Say M if the target processor supports
+ PCI and/or PCIe.
+
+config XILLYBUS_OF
+ tristate "Xillybus over Device Tree"
+ depends on XILLYBUS && OF_ADDRESS && OF_DEVICE && OF_IRQ
+ default n
+ help
+ Set to M if you want Xillybus to find its resources from the
+ Open Firmware Flattened Device Tree. If the target is an embedded
+ system, say M. This option is harmless, but it requires Device
+ Tree support on the kernel, which is usually not the case for
+ kernels for fullblown computers.
+
endif
diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile
index b354c53..10717cc 100644
--- a/drivers/uio/Makefile
+++ b/drivers/uio/Makefile
@@ -8,3 +8,6 @@ obj-$(CONFIG_UIO_SERCOS3) += uio_sercos3.o
obj-$(CONFIG_UIO_PCI_GENERIC) += uio_pci_generic.o
obj-$(CONFIG_UIO_NETX) += uio_netx.o
obj-$(CONFIG_UIO_PRUSS) += uio_pruss.o
+obj-$(CONFIG_XILLYBUS) += xillybus_core.o
+obj-$(CONFIG_XILLYBUS_PCIE) += xillybus_pcie.o
+obj-$(CONFIG_XILLYBUS_OF) += xillybus_of.o
diff --git a/drivers/uio/xillybus.h b/drivers/uio/xillybus.h
new file mode 100644
index 0000000..c260ebc
--- /dev/null
+++ b/drivers/uio/xillybus.h
@@ -0,0 +1,185 @@
+/*
+ * linux/drivers/misc/xillybus.h
+ *
+ * Copyright 2011 Xillybus Ltd, http://xillybus.com
+ *
+ * Header file for the Xillybus FPGA/host framework.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the smems of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#ifndef __XILLYBUS_H
+#define __XILLYBUS_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+char xillyname[] = "xillybus";
+
+struct xilly_endpoint_hardware;
+
+struct xilly_page {
+ struct list_head node;
+ unsigned long addr;
+ unsigned int order;
+};
+
+struct xilly_dma {
+ struct list_head node;
+ struct pci_dev *pdev;
+ struct device *dev;
+ dma_addr_t dma_addr;
+ size_t size;
+ int direction;
+};
+
+struct xilly_buffer {
+ void *addr;
+ dma_addr_t dma_addr;
+ int end_offset; /* Counting elements, not bytes */
+};
+
+struct xilly_cleanup {
+ struct list_head to_kfree;
+ struct list_head to_pagefree;
+ struct list_head to_unmap;
+};
+
+struct xilly_idt_handle {
+ unsigned char *chandesc;
+ unsigned char *idt;
+ int entries;
+};
+
+/*
+ * Read-write confusion: wr_* and rd_* notation sticks to FPGA view, so
+ * wr_* buffers are those consumed by read(), since the FPGA writes to them
+ * and vice versa.
+ */
+
+struct xilly_channel {
+ struct xilly_endpoint *endpoint;
+ int chan_num;
+ int log2_element_size;
+ int seekable;
+
+ struct xilly_buffer **wr_buffers; /* FPGA writes, driver reads! */
+ int num_wr_buffers;
+ unsigned int wr_buf_size; /* In bytes */
+ int wr_fpga_buf_idx;
+ int wr_host_buf_idx;
+ int wr_host_buf_pos;
+ int wr_empty;
+ int wr_ready; /* Significant only when wr_empty == 1 */
+ int wr_sleepy;
+ int wr_eof;
+ int wr_hangup;
+ spinlock_t wr_spinlock;
+ struct mutex wr_mutex;
+ wait_queue_head_t wr_wait;
+ wait_queue_head_t wr_ready_wait;
+ int wr_ref_count;
+ int wr_synchronous;
+ int wr_allow_partial;
+ int wr_exclusive_open;
+ int wr_supports_nonempty;
+
+ struct xilly_buffer **rd_buffers; /* FPGA reads, driver writes! */
+ int num_rd_buffers;
+ unsigned int rd_buf_size; /* In bytes */
+ int rd_fpga_buf_idx;
+ int rd_host_buf_pos;
+ int rd_host_buf_idx;
+ int rd_full;
+ spinlock_t rd_spinlock;
+ struct mutex rd_mutex;
+ wait_queue_head_t rd_wait;
+ int rd_ref_count;
+ int rd_allow_partial;
+ int rd_synchronous;
+ int rd_exclusive_open;
+ struct delayed_work rd_workitem;
+ unsigned char rd_leftovers[4];
+};
+
+struct xilly_endpoint {
+ /*
+ * One of pdev and dev is always NULL, and the other is a valid
+ * pointer, depending on the type of device
+ */
+ struct pci_dev *pdev;
+ struct device *dev;
+ struct resource res; /* OF devices only */
+ struct xilly_endpoint_hardware *ephw;
+
+ struct list_head ep_list;
+ int dma_using_dac; /* =1 if 64-bit DMA is used, =0 otherwise. */
+ u32 *registers;
+ int fatal_error;
+
+ struct mutex register_mutex;
+ wait_queue_head_t ep_wait;
+
+ /* List of memory allocations, to make release easy */
+ struct xilly_cleanup cleanup;
+
+ /* Channels and message handling */
+ struct cdev cdev;
+
+ int major;
+ int lowest_minor; /* Highest minor = lowest_minor + num_channels - 1 */
+
+ int num_channels; /* EXCLUDING message buffer */
+ struct xilly_channel **channels;
+ int msg_counter;
+ int failed_messages;
+ int idtlen;
+
+ u32 *msgbuf_addr;
+ dma_addr_t msgbuf_dma_addr;
+ unsigned int msg_buf_size;
+};
+
+struct xilly_endpoint_hardware {
+ struct module *owner;
+ void (*sync_single_for_cpu)(struct xilly_endpoint *,
+ dma_addr_t,
+ size_t,
+ int);
+ void (*sync_single_for_device)(struct xilly_endpoint *,
+ dma_addr_t,
+ size_t,
+ int);
+ dma_addr_t (*map_single)(struct xilly_cleanup *,
+ struct xilly_endpoint *,
+ void *,
+ size_t,
+ int);
+ void (*unmap_single)(struct xilly_dma *entry);
+};
+
+irqreturn_t xillybus_isr(int irq, void *data);
+
+void xillybus_do_cleanup(struct xilly_cleanup *mem,
+ struct xilly_endpoint *endpoint);
+
+struct xilly_endpoint *xillybus_init_endpoint(struct pci_dev *pdev,
+ struct device *dev,
+ struct xilly_endpoint_hardware
+ *ephw);
+
+int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint);
+
+void xillybus_endpoint_remove(struct xilly_endpoint *endpoint);
+
+#endif /* __XILLYBUS_H */
diff --git a/drivers/uio/xillybus_core.c b/drivers/uio/xillybus_core.c
new file mode 100644
index 0000000..dd0a71c
--- /dev/null
+++ b/drivers/uio/xillybus_core.c
@@ -0,0 +1,2345 @@
+/*
+ * linux/drivers/misc/xillybus_core.c
+ *
+ * Copyright 2011 Xillybus Ltd, http://xillybus.com
+ *
+ * Driver for the Xillybus FPGA/host framework.
+ *
+ * This driver interfaces with a special IP core in an FPGA, setting up
+ * a pipe between a hardware FIFO in the programmable logic and a device
+ * file in the host. The number of such pipes and their attributes are
+ * set up on the logic. This driver detects these automatically and
+ * creates the device files accordingly.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the smems of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "xillybus.h"
+
+MODULE_DESCRIPTION("Xillybus core functions");
+MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
+MODULE_VERSION("1.07");
+MODULE_ALIAS("xillybus_core");
+MODULE_LICENSE("GPL v2");
+
+/* General timeout is 100 ms, rx timeout is 10 ms */
+#define XILLY_RX_TIMEOUT (10*HZ/1000)
+#define XILLY_TIMEOUT (100*HZ/1000)
+
+#define fpga_msg_ctrl_reg 0x0002
+#define fpga_dma_control_reg 0x0008
+#define fpga_dma_bufno_reg 0x0009
+#define fpga_dma_bufaddr_lowaddr_reg 0x000a
+#define fpga_dma_bufaddr_highaddr_reg 0x000b
+#define fpga_buf_ctrl_reg 0x000c
+#define fpga_buf_offset_reg 0x000d
+#define fpga_endian_reg 0x0010
+
+#define XILLYMSG_OPCODE_RELEASEBUF 1
+#define XILLYMSG_OPCODE_QUIESCEACK 2
+#define XILLYMSG_OPCODE_FIFOEOF 3
+#define XILLYMSG_OPCODE_FATAL_ERROR 4
+#define XILLYMSG_OPCODE_NONEMPTY 5
+
+static struct class *xillybus_class;
+
+/*
+ * ep_list_lock is the last lock to be taken; No other lock requests are
+ * allowed while holding it. It merely protects list_of_endpoints, and not
+ * the endpoints listed in it.
+ */
+
+static LIST_HEAD(list_of_endpoints);
+static struct mutex ep_list_lock;
+struct workqueue_struct *xillybus_wq;
+
+/*
+ * Locking scheme: Mutexes protect invocations of character device methods.
+ * If both locks are taken, wr_mutex is taken first, rd_mutex second.
+ *
+ * wr_spinlock protects wr_*_buf_idx, wr_empty, wr_sleepy, wr_ready and the
+ * buffers' end_offset fields against changes made by IRQ handler (and in
+ * theory, other file request handlers, but the mutex handles that). Nothing
+ * else.
+ * They are held for short direct memory manipulations. Needless to say,
+ * no mutex locking is allowed when a spinlock is held.
+ *
+ * rd_spinlock does the same with rd_*_buf_idx, rd_empty and end_offset.
+ *
+ * register_mutex is endpoint-specific, and is held when non-atomic
+ * register operations are performed. wr_mutex and rd_mutex may be
+ * held when register_mutex is taken, but none of the spinlocks. Note that
+ * register_mutex doesn't protect against sporadic buf_ctrl_reg writes
+ * which are unrelated to buf_offset_reg, since they are harmless.
+ *
+ * Blocking on the wait queues is allowed with mutexes held, but not with
+ * spinlocks.
+ *
+ * Only interruptible blocking is allowed on mutexes and wait queues.
+ *
+ * All in all, the locking order goes (with skips allowed, of course):
+ * wr_mutex -> rd_mutex -> register_mutex -> wr_spinlock -> rd_spinlock
+ */
+
+static void malformed_message(u32 *buf)
+{
+ int opcode;
+ int msg_channel, msg_bufno, msg_data, msg_dir;
+
+ opcode = (buf[0] >> 24) & 0xff;
+ msg_dir = buf[0] & 1;
+ msg_channel = (buf[0] >> 1) & 0x7ff;
+ msg_bufno = (buf[0] >> 12) & 0x3ff;
+ msg_data = buf[1] & 0xfffffff;
+
+ pr_warn("xillybus: Malformed message (skipping): "
+ "opcode=%d, channel=%03x, dir=%d, bufno=%03x, data=%07x\n",
+ opcode, msg_channel, msg_dir, msg_bufno, msg_data);
+}
+
+/*
+ * xillybus_isr assumes the interrupt is allocated exclusively to it,
+ * which is the natural case MSI and several other hardware-oriented
+ * interrupts. Sharing is not allowed.
+ */
+
+irqreturn_t xillybus_isr(int irq, void *data)
+{
+ struct xilly_endpoint *ep = data;
+ u32 *buf;
+ unsigned int buf_size;
+ int i;
+ int opcode;
+ unsigned int msg_channel, msg_bufno, msg_data, msg_dir;
+ struct xilly_channel *channel;
+
+ /*
+ * The endpoint structure is altered during periods when it's
+ * guaranteed no interrupt will occur, but in theory, the cache
+ * lines may not be updated. So a memory barrier is issued.
+ */
+
+ smp_rmb();
+
+ buf = ep->msgbuf_addr;
+ buf_size = ep->msg_buf_size/sizeof(u32);
+
+
+ ep->ephw->sync_single_for_cpu(ep,
+ ep->msgbuf_dma_addr,
+ ep->msg_buf_size,
+ DMA_FROM_DEVICE);
+
+ for (i = 0; i
> 28) & 0xf) != ep->msg_counter) {
+ malformed_message(&buf);
+ pr_warn("xillybus: Sending a NACK on "
+ "counter %x (instead of %x) on entry %d\n",
+ ((buf[i+1] >> 28) & 0xf),
+ ep->msg_counter,
+ i/2);
+
+ if (++ep->failed_messages > 10)
+ pr_err("xillybus: Lost sync with "
+ "interrupt messages. Stopping.\n");
+ else {
+ ep->ephw->sync_single_for_device(
+ ep,
+ ep->msgbuf_dma_addr,
+ ep->msg_buf_size,
+ DMA_FROM_DEVICE);
+
+ iowrite32(0x01, /* Message NACK */
+ &ep->registers[fpga_msg_ctrl_reg]);
+ }
+ return IRQ_HANDLED;
+ } else if (buf & (1
= buf_size) {
+ pr_err("xillybus: Bad interrupt message. Stopping.\n");
+ return IRQ_HANDLED;
+ }
+
+ buf_size = i;
+
+ for (i = 0; i
> 24) & 0xff;
+
+ msg_dir = buf & 1;
+ msg_channel = (buf >> 1) & 0x7ff;
+ msg_bufno = (buf >> 12) & 0x3ff;
+ msg_data = buf[i+1] & 0xfffffff;
+
+ switch (opcode) {
+ case XILLYMSG_OPCODE_RELEASEBUF:
+
+ if ((msg_channel > ep->num_channels) ||
+ (msg_channel == 0)) {
+ malformed_message(&buf);
+ break;
+ }
+
+ channel = ep->channels[msg_channel];
+
+ if (msg_dir) { /* Write channel */
+ if (msg_bufno >= channel->num_wr_buffers) {
+ malformed_message(&buf);
+ break;
+ }
+ spin_lock(&channel->wr_spinlock);
+ channel->wr_buffers[msg_bufno]->end_offset =
+ msg_data;
+ channel->wr_fpga_buf_idx = msg_bufno;
+ channel->wr_empty = 0;
+ channel->wr_sleepy = 0;
+ spin_unlock(&channel->wr_spinlock);
+
+ wake_up_interruptible(&channel->wr_wait);
+
+ } else {
+ /* Read channel */
+
+ if (msg_bufno >= channel->num_rd_buffers) {
+ malformed_message(&buf);
+ break;
+ }
+
+ spin_lock(&channel->rd_spinlock);
+ channel->rd_fpga_buf_idx = msg_bufno;
+ channel->rd_full = 0;
+ spin_unlock(&channel->rd_spinlock);
+
+ wake_up_interruptible(&channel->rd_wait);
+ if (!channel->rd_synchronous)
+ queue_delayed_work(
+ xillybus_wq,
+ &channel->rd_workitem,
+ XILLY_RX_TIMEOUT);
+ }
+
+ break;
+ case XILLYMSG_OPCODE_NONEMPTY:
+ if ((msg_channel > ep->num_channels) ||
+ (msg_channel == 0) || (!msg_dir) ||
+ !ep->channels[msg_channel]->wr_supports_nonempty) {
+ malformed_message(&buf);
+ break;
+ }
+
+ channel = ep->channels[msg_channel];
+
+ if (msg_bufno >= channel->num_wr_buffers) {
+ malformed_message(&buf);
+ break;
+ }
+ spin_lock(&channel->wr_spinlock);
+ if (msg_bufno == channel->wr_host_buf_idx)
+ channel->wr_ready = 1;
+ spin_unlock(&channel->wr_spinlock);
+
+ wake_up_interruptible(&channel->wr_ready_wait);
+
+ break;
+ case XILLYMSG_OPCODE_QUIESCEACK:
+ ep->idtlen = msg_data;
+ wake_up_interruptible(&ep->ep_wait);
+
+ break;
+ case XILLYMSG_OPCODE_FIFOEOF:
+ channel = ep->channels[msg_channel];
+ spin_lock(&channel->wr_spinlock);
+ channel->wr_eof = msg_bufno;
+ channel->wr_sleepy = 0;
+
+ channel->wr_hangup = channel->wr_empty &&
+ (channel->wr_host_buf_idx == msg_bufno);
+
+ spin_unlock(&channel->wr_spinlock);
+
+ wake_up_interruptible(&channel->wr_wait);
+
+ break;
+ case XILLYMSG_OPCODE_FATAL_ERROR:
+ ep->fatal_error = 1;
+ wake_up_interruptible(&ep->ep_wait); /* For select() */
+ pr_err("xillybus: FPGA reported a fatal "
+ "error. This means that the low-level "
+ "communication with the device has failed. "
+ "This hardware problem is most likely "
+ "unrelated to xillybus (neither kernel "
+ "module nor FPGA core), but reports are "
+ "still welcome. All I/O is aborted.\n");
+ break;
+ default:
+ malformed_message(&buf);
+ break;
+ }
+ }
+
+ ep->ephw->sync_single_for_device(ep,
+ ep->msgbuf_dma_addr,
+ ep->msg_buf_size,
+ DMA_FROM_DEVICE);
+
+ ep->msg_counter = (ep->msg_counter + 1) & 0xf;
+ ep->failed_messages = 0;
+ iowrite32(0x03, &ep->registers[fpga_msg_ctrl_reg]); /* Message ACK */
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(xillybus_isr);
+
+/*
+ * A few trivial memory management functions.
+ * NOTE: These functions are used only on probe and remove, and therefore
+ * no locks are applied!
+ */
+
+void xillybus_do_cleanup(struct xilly_cleanup *mem,
+ struct xilly_endpoint *endpoint)
+{
+ struct list_head *this, *next;
+
+ list_for_each_safe(this, next, &mem->to_unmap) {
+ struct xilly_dma *entry =
+ list_entry(this, struct xilly_dma, node);
+
+ endpoint->ephw->unmap_single(entry);
+ kfree(entry);
+ }
+
+ INIT_LIST_HEAD(&mem->to_unmap);
+
+ list_for_each_safe(this, next, &mem->to_kfree)
+ kfree(this);
+
+ INIT_LIST_HEAD(&mem->to_kfree);
+
+ list_for_each_safe(this, next, &mem->to_pagefree) {
+ struct xilly_page *entry =
+ list_entry(this, struct xilly_page, node);
+
+ free_pages(entry->addr, entry->order);
+ kfree(entry);
+ }
+ INIT_LIST_HEAD(&mem->to_pagefree);
+}
+EXPORT_SYMBOL(xillybus_do_cleanup);
+
+static void *xilly_malloc(struct xilly_cleanup *mem, size_t size)
+{
+ void *ptr;
+
+ ptr = kzalloc(sizeof(struct list_head) + size, GFP_KERNEL);
+
+ if (!ptr)
+ return ptr;
+
+ list_add_tail((struct list_head *) ptr, &mem->to_kfree);
+
+ return ptr + sizeof(struct list_head);
+}
+
+static unsigned long xilly_pagealloc(struct xilly_cleanup *mem,
+ unsigned long order)
+{
+ unsigned long addr;
+ struct xilly_page *this;
+
+ this = kmalloc(sizeof(struct xilly_page), GFP_KERNEL);
+ if (!this)
+ return 0;
+
+ addr = __get_free_pages(GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO, order);
+
+ if (!addr) {
+ kfree(this);
+ return 0;
+ }
+
+ this->addr = addr;
+ this->order = order;
+
+ list_add_tail(&this->node, &mem->to_pagefree);
+
+ return addr;
+}
+
+
+static void xillybus_autoflush(struct work_struct *work);
+
+static int xilly_setupchannels(struct xilly_endpoint *ep,
+ struct xilly_cleanup *mem,
+ unsigned char *chandesc,
+ int entries
+ )
+{
+ int i, entry, wr_nbuffer, rd_nbuffer;
+ struct xilly_channel *channel;
+ int channelnum, bufnum, bufsize, format, is_writebuf;
+ int bytebufsize;
+ int synchronous, allowpartial, exclusive_open, seekable;
+ int supports_nonempty;
+ void *wr_salami = NULL;
+ void *rd_salami = NULL;
+ int left_of_wr_salami = 0;
+ int left_of_rd_salami = 0;
+ dma_addr_t dma_addr;
+ int msg_buf_done = 0;
+
+ struct xilly_buffer *this_buffer = NULL; /* Init to silence warning */
+
+ channel = xilly_malloc(mem, ep->num_channels *
+ sizeof(struct xilly_channel));
+
+ if (!channel)
+ goto memfail;
+
+ ep->channels = xilly_malloc(mem, (ep->num_channels + 1) *
+ sizeof(struct xilly_channel *));
+
+ if (!ep->channels)
+ goto memfail;
+
+ ep->channels[0] = NULL; /* Channel 0 is message buf. */
+
+ /* Initialize all channels with defaults */
+
+ for (i = 1; i
num_channels; i++) {
+ channel->wr_buffers = NULL;
+ channel->rd_buffers = NULL;
+ channel->num_wr_buffers = 0;
+ channel->num_rd_buffers = 0;
+ channel->wr_fpga_buf_idx = -1;
+ channel->wr_host_buf_idx = 0;
+ channel->wr_host_buf_pos = 0;
+ channel->wr_empty = 1;
+ channel->wr_ready = 0;
+ channel->wr_sleepy = 1;
+ channel->rd_fpga_buf_idx = 0;
+ channel->rd_host_buf_idx = 0;
+ channel->rd_host_buf_pos = 0;
+ channel->rd_full = 0;
+ channel->wr_ref_count = 0;
+ channel->rd_ref_count = 0;
+
+ spin_lock_init(&channel->wr_spinlock);
+ spin_lock_init(&channel->rd_spinlock);
+ mutex_init(&channel->wr_mutex);
+ mutex_init(&channel->rd_mutex);
+ init_waitqueue_head(&channel->rd_wait);
+ init_waitqueue_head(&channel->wr_wait);
+ init_waitqueue_head(&channel->wr_ready_wait);
+
+ INIT_DELAYED_WORK(&channel->rd_workitem, xillybus_autoflush);
+
+ channel->endpoint = ep;
+ channel->chan_num = i;
+
+ channel->log2_element_size = 0;
+
+ ep->channels = channel++;
+ }
+
+ /*
+ * The DMA buffer address update is atomic on the FPGA, so even if
+ * it was in the middle of sending messages to some buffer, changing
+ * the address is safe, since the data will go to either of the
+ * buffers. Not that this situation should occur at all anyhow.
+ */
+
+ wr_nbuffer = 1;
+ rd_nbuffer = 1; /* Buffer zero isn't used at all */
+
+ for (entry = 0; entry
> 1) | ((chandesc[1] & 0x0f)
> 4) & 0x03;
+ allowpartial = (chandesc[1] >> 6) & 0x01;
+ synchronous = (chandesc[1] >> 7) & 0x01;
+ bufsize = 1
> 7) & 0x01;
+ seekable = (chandesc[2] >> 6) & 0x01;
+ supports_nonempty = (chandesc[2] >> 5) & 0x01;
+
+ if ((channelnum > ep->num_channels) ||
+ ((channelnum == 0) && !is_writebuf)) {
+ pr_err("xillybus: IDT requests channel out "
+ "of range. Aborting.\n");
+ return -ENODEV;
+ }
+
+ channel = ep->channels[channelnum]; /* NULL for msg channel */
+
+ bytebufsize = bufsize
num_rd_buffers = bufnum;
+ channel->log2_element_size = ((format > 2) ?
+ 2 : format);
+ bytebufsize = channel->rd_buf_size = bufsize *
+ (1
log2_element_size);
+ channel->rd_allow_partial = allowpartial;
+ channel->rd_synchronous = synchronous;
+ channel->rd_exclusive_open = exclusive_open;
+ channel->seekable = seekable;
+
+ channel->rd_buffers = xilly_malloc(
+ mem,
+ bufnum * sizeof(struct xilly_buffer *));
+
+ if (!channel->rd_buffers)
+ goto memfail;
+
+ this_buffer = xilly_malloc(
+ mem,
+ bufnum * sizeof(struct xilly_buffer));
+
+ if (!this_buffer)
+ goto memfail;
+ }
+
+ else if (channelnum > 0) {
+ channel->num_wr_buffers = bufnum;
+ channel->log2_element_size = ((format > 2) ?
+ 2 : format);
+ bytebufsize = channel->wr_buf_size = bufsize *
+ (1
log2_element_size);
+
+ channel->seekable = seekable;
+ channel->wr_supports_nonempty = supports_nonempty;
+
+ channel->wr_allow_partial = allowpartial;
+ channel->wr_synchronous = synchronous;
+ channel->wr_exclusive_open = exclusive_open;
+
+ channel->wr_buffers = xilly_malloc(
+ mem,
+ bufnum * sizeof(struct xilly_buffer *));
+
+ if (!channel->wr_buffers)
+ goto memfail;
+
+ this_buffer = xilly_malloc(
+ mem,
+ bufnum * sizeof(struct xilly_buffer));
+
+ if (!this_buffer)
+ goto memfail;
+ }
+
+ /*
+ * Although daunting, we cut the chunks for read buffers
+ * from a different salami than the write buffers',
+ * possibly improving performance.
+ */
+
+ if (is_writebuf)
+ for (i = 0; i
0)) {
+ pr_err("xillybus: "
+ "Corrupt buffer allocation "
+ "in IDT. Aborting.\n");
+ return -ENODEV;
+ }
+
+ if (left_of_wr_salami == 0) {
+ int allocorder, allocsize;
+
+ allocsize = PAGE_SIZE;
+ allocorder = 0;
+ while (bytebufsize > allocsize) {
+ allocsize *= 2;
+ allocorder++;
+ }
+
+ wr_salami = (void *)
+ xilly_pagealloc(mem,
+ allocorder);
+ if (!wr_salami)
+ goto memfail;
+ left_of_wr_salami = allocsize;
+ }
+
+ dma_addr = ep->ephw->map_single(
+ mem,
+ ep,
+ wr_salami,
+ bytebufsize,
+ DMA_FROM_DEVICE);
+
+ if (!dma_addr)
+ goto dmafail;
+
+ iowrite32(
+ (u32) (dma_addr & 0xffffffff),
+ &ep->registers[
+ fpga_dma_bufaddr_lowaddr_reg]
+ );
+ iowrite32(
+ ((u32) ((((u64) dma_addr) >> 32)
+ & 0xffffffff)),
+ &ep->registers[
+ fpga_dma_bufaddr_highaddr_reg]
+ );
+ mmiowb();
+
+ if (channelnum > 0) {
+ this_buffer->addr = wr_salami;
+ this_buffer->dma_addr = dma_addr;
+ channel->wr_buffers = this_buffer++;
+
+ iowrite32(
+ 0x80000000 | wr_nbuffer++,
+ &ep->registers[
+ fpga_dma_bufno_reg]);
+ } else {
+ ep->msgbuf_addr = wr_salami;
+ ep->msgbuf_dma_addr = dma_addr;
+ ep->msg_buf_size = bytebufsize;
+ msg_buf_done++;
+
+ iowrite32(
+ 0x80000000, &ep->registers[
+ fpga_dma_bufno_reg]);
+ }
+
+ left_of_wr_salami -= bytebufsize;
+ wr_salami += bytebufsize;
+ }
+ else /* Read buffers */
+ for (i = 0; i
0)) {
+ pr_err("xillybus: "
+ "Corrupt buffer allocation "
+ "in IDT. Aborting.\n");
+ return -ENODEV;
+ }
+
+ if (left_of_rd_salami == 0) {
+ int allocorder, allocsize;
+
+ allocsize = PAGE_SIZE;
+ allocorder = 0;
+ while (bytebufsize > allocsize) {
+ allocsize *= 2;
+ allocorder++;
+ }
+
+ rd_salami = (void *)
+ xilly_pagealloc(
+ mem,
+ allocorder);
+
+ if (!rd_salami)
+ goto memfail;
+ left_of_rd_salami = allocsize;
+ }
+
+ dma_addr = ep->ephw->map_single(
+ mem,
+ ep,
+ rd_salami,
+ bytebufsize,
+ DMA_TO_DEVICE);
+
+ if (!dma_addr)
+ goto dmafail;
+
+ iowrite32(
+ (u32) (dma_addr & 0xffffffff),
+ &ep->registers[
+ fpga_dma_bufaddr_lowaddr_reg]
+ );
+ iowrite32(
+ ((u32) ((((u64) dma_addr) >> 32)
+ & 0xffffffff)),
+ &ep->registers[
+ fpga_dma_bufaddr_highaddr_reg]
+ );
+ mmiowb();
+
+ this_buffer->addr = rd_salami;
+ this_buffer->dma_addr = dma_addr;
+ channel->rd_buffers = this_buffer++;
+
+ iowrite32(rd_nbuffer++,
+ &ep->registers[fpga_dma_bufno_reg]);
+
+ left_of_rd_salami -= bytebufsize;
+ rd_salami += bytebufsize;
+ }
+ }
+
+ if (!msg_buf_done) {
+ pr_err("xillybus: Corrupt IDT: No message buffer. "
+ "Aborting.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+
+memfail:
+ pr_err("xillybus: Failed to allocate write buffer memory. "
+ "Aborting.\n");
+ return -ENOMEM;
+dmafail:
+ pr_err("xillybus: Failed to map DMA memory!. Aborting.\n");
+ return -ENOMEM;
+}
+
+static void xilly_scan_idt(struct xilly_endpoint *endpoint,
+ struct xilly_idt_handle *idt_handle)
+{
+ int count = 0;
+ unsigned char *idt = endpoint->channels[1]->wr_buffers[0]->addr;
+ unsigned char *end_of_idt = idt + endpoint->idtlen - 4;
+ unsigned char *scan;
+ int len;
+
+ scan = idt;
+ idt_handle->idt = idt;
+
+ scan++; /* Skip version number */
+
+ while ((scan
end_of_idt) {
+ pr_err("xillybus: IDT device name list overflow. "
+ "Aborting.\n");
+ idt_handle->chandesc = NULL;
+ return;
+ } else
+ idt_handle->chandesc = scan;
+
+ len = endpoint->idtlen - (3 + ((int) (scan - idt)));
+
+ if (len & 0x03) {
+ idt_handle->chandesc = NULL;
+
+ pr_err("xillybus: Corrupt IDT device name list. "
+ "Aborting.\n");
+ }
+
+ idt_handle->entries = len >> 2;
+
+ endpoint->num_channels = count;
+}
+
+static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
+{
+ int rc = 0;
+ struct xilly_channel *channel;
+ unsigned char *version;
+
+ channel = endpoint->channels[1]; /* This should be generated ad-hoc */
+
+ channel->wr_sleepy = 1;
+ wmb(); /* Setting wr_sleepy must come before the command */
+
+ iowrite32(1 |
+ (3
registers[fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+
+ wait_event_interruptible_timeout(channel->wr_wait,
+ (!channel->wr_sleepy),
+ XILLY_TIMEOUT);
+
+ if (channel->wr_sleepy) {
+ pr_err("xillybus: Failed to obtain IDT. Aborting.\n");
+
+ if (endpoint->fatal_error)
+ return -EIO;
+
+ rc = -ENODEV;
+ return rc;
+ }
+
+ endpoint->ephw->sync_single_for_cpu(
+ channel->endpoint,
+ channel->wr_buffers[0]->dma_addr,
+ channel->wr_buf_size,
+ DMA_FROM_DEVICE);
+
+ if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) {
+ pr_err("xillybus: IDT length mismatch (%d != %d). "
+ "Aborting.\n",
+ channel->wr_buffers[0]->end_offset, endpoint->idtlen);
+ rc = -ENODEV;
+ return rc;
+ }
+
+ if (crc32_le(~0, channel->wr_buffers[0]->addr,
+ endpoint->idtlen+1) != 0) {
+ pr_err("xillybus: IDT failed CRC check. Aborting.\n");
+ rc = -ENODEV;
+ return rc;
+ }
+
+ version = channel->wr_buffers[0]->addr;
+
+ /* Check version number. Accept anything below 0x82 for now. */
+ if (*version > 0x82) {
+ pr_err("xillybus: No support for IDT version 0x%02x. "
+ "Maybe the xillybus driver needs an upgarde. "
+ "Aborting.\n",
+ (int) *version);
+ rc = -ENODEV;
+ return rc;
+ }
+
+ return 0; /* Success */
+}
+
+static ssize_t xillybus_read(struct file *filp, char *userbuf, size_t count,
+ loff_t *f_pos)
+{
+ ssize_t rc;
+ unsigned long flags;
+ int bytes_done = 0;
+ int no_time_left = 0;
+ long deadline, left_to_sleep;
+ struct xilly_channel *channel = filp->private_data;
+
+ int empty, reached_eof, exhausted, ready;
+ /* Initializations are there only to silence warnings */
+
+ int howmany = 0, bufpos = 0, bufidx = 0, bufferdone = 0;
+ int waiting_bufidx;
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ deadline = jiffies + 1 + XILLY_RX_TIMEOUT;
+
+ rc = mutex_lock_interruptible(&channel->wr_mutex);
+
+ if (rc)
+ return rc;
+
+ rc = 0; /* Just to be clear about it. Compiler optimizes this out */
+
+ while (1) { /* Note that we may drop mutex within this loop */
+ int bytes_to_do = count - bytes_done;
+ spin_lock_irqsave(&channel->wr_spinlock, flags);
+
+ empty = channel->wr_empty;
+ ready = !empty || channel->wr_ready;
+
+ if (!empty) {
+ bufidx = channel->wr_host_buf_idx;
+ bufpos = channel->wr_host_buf_pos;
+ howmany = ((channel->wr_buffers[bufidx]->end_offset
+ + 1)
log2_element_size)
+ - bufpos;
+
+ /* Update wr_host_* to its post-operation state */
+ if (howmany > bytes_to_do) {
+ bufferdone = 0;
+
+ howmany = bytes_to_do;
+ channel->wr_host_buf_pos += howmany;
+ } else {
+ bufferdone = 1;
+
+ channel->wr_host_buf_pos = 0;
+
+ if (bufidx == channel->wr_fpga_buf_idx) {
+ channel->wr_empty = 1;
+ channel->wr_sleepy = 1;
+ channel->wr_ready = 0;
+ }
+
+ if (bufidx >= (channel->num_wr_buffers - 1))
+ channel->wr_host_buf_idx = 0;
+ else
+ channel->wr_host_buf_idx++;
+ }
+ }
+
+ /*
+ * Marking our situation after the possible changes above,
+ * for use after releasing the spinlock.
+ *
+ * empty = empty before change
+ * exhasted = empty after possible change
+ */
+
+ reached_eof = channel->wr_empty &&
+ (channel->wr_host_buf_idx == channel->wr_eof);
+ channel->wr_hangup = reached_eof;
+ exhausted = channel->wr_empty;
+ waiting_bufidx = channel->wr_host_buf_idx;
+
+ spin_unlock_irqrestore(&channel->wr_spinlock, flags);
+
+ if (!empty) { /* Go on, now without the spinlock */
+
+ if (bufpos == 0) /* Position zero means it's virgin */
+ channel->endpoint->ephw->sync_single_for_cpu(
+ channel->endpoint,
+ channel->wr_buffers[bufidx]->dma_addr,
+ channel->wr_buf_size,
+ DMA_FROM_DEVICE);
+
+ if (copy_to_user(
+ userbuf,
+ channel->wr_buffers[bufidx]->addr
+ + bufpos, howmany))
+ rc = -EFAULT;
+
+ userbuf += howmany;
+ bytes_done += howmany;
+
+ if (bufferdone) {
+ channel->endpoint->ephw->
+ sync_single_for_device
+ (
+ channel->endpoint,
+ channel->wr_buffers[bufidx]->
+ dma_addr,
+ channel->wr_buf_size,
+ DMA_FROM_DEVICE);
+
+ /*
+ * Tell FPGA the buffer is done with. It's an
+ * atomic operation to the FPGA, so what
+ * happens with other channels doesn't matter,
+ * and the certain channel is protected with
+ * the channel-specific mutex.
+ */
+
+ iowrite32(1 | (channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+ }
+
+ if (rc) {
+ mutex_unlock(&channel->wr_mutex);
+ return rc;
+ }
+ }
+
+ /* This includes a zero-count return = EOF */
+ if ((bytes_done >= count) || reached_eof)
+ break;
+
+ if (!exhausted)
+ continue; /* More in RAM buffer(s)? Just go on. */
+
+ if ((bytes_done > 0) &&
+ (no_time_left ||
+ (channel->wr_synchronous && channel->wr_allow_partial)))
+ break;
+
+ /*
+ * Nonblocking read: The "ready" flag tells us that the FPGA
+ * has data to send. In non-blocking mode, if it isn't on,
+ * just return. But if there is, we jump directly to the point
+ * where we ask for the FPGA to send all it has, and wait
+ * until that data arrives. So in a sense, we *do* block in
+ * nonblocking mode, but only for a very short time.
+ */
+
+ if (!no_time_left && (filp->f_flags & O_NONBLOCK)) {
+ if (bytes_done > 0)
+ break;
+
+ if (ready)
+ goto desperate;
+
+ bytes_done = -EAGAIN;
+ break;
+ }
+
+ if (!no_time_left || (bytes_done > 0)) {
+ /*
+ * Note that in case of an element-misaligned read
+ * request, offsetlimit will include the last element,
+ * which will be partially read from.
+ */
+ int offsetlimit = ((count - bytes_done) - 1) >>
+ channel->log2_element_size;
+ int buf_elements = channel->wr_buf_size >>
+ channel->log2_element_size;
+
+ /*
+ * In synchronous mode, always send an offset limit.
+ * Just don't send a value too big.
+ */
+
+ if (channel->wr_synchronous) {
+ /* Don't request more than one buffer */
+ if (channel->wr_allow_partial &&
+ (offsetlimit >= buf_elements))
+ offsetlimit = buf_elements - 1;
+
+ /* Don't request more than all buffers */
+ if (!channel->wr_allow_partial &&
+ (offsetlimit >=
+ (buf_elements * channel->num_wr_buffers)))
+ offsetlimit = buf_elements *
+ channel->num_wr_buffers - 1;
+ }
+
+ /*
+ * In asynchronous mode, force early flush of a buffer
+ * only if that will allow returning a full count. The
+ * "offsetlimit
wr_synchronous ||
+ (offsetlimit
endpoint->register_mutex);
+
+ iowrite32(offsetlimit,
+ &channel->endpoint->registers[
+ fpga_buf_offset_reg]);
+ mmiowb();
+
+ iowrite32(1 | (channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+
+ mmiowb(); /* Just to appear safe */
+
+ mutex_unlock(&channel->endpoint->
+ register_mutex);
+ }
+
+ }
+
+ /*
+ * If partial completion is disallowed, there is no point in
+ * timeout sleeping. Neither if no_time_left is set and
+ * there's no data.
+ */
+
+ if (!channel->wr_allow_partial ||
+ (no_time_left && (bytes_done == 0))) {
+
+ /*
+ * This do-loop will run more than once if another
+ * thread reasserted wr_sleepy before we got the mutex
+ * back, so we try again.
+ */
+
+ do {
+ mutex_unlock(&channel->wr_mutex);
+
+ if (wait_event_interruptible(
+ channel->wr_wait,
+ (!channel->wr_sleepy)))
+ goto interrupted;
+
+ if (mutex_lock_interruptible(
+ &channel->wr_mutex))
+ goto interrupted;
+ } while (channel->wr_sleepy);
+
+ continue;
+
+interrupted: /* Mutex is not held if got here */
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+ if (bytes_done)
+ return bytes_done;
+ if (filp->f_flags & O_NONBLOCK)
+ return -EAGAIN; /* Don't admit snoozing */
+ return -EINTR;
+ }
+
+ left_to_sleep = deadline - ((long) jiffies);
+
+ /*
+ * If our time is out, skip the waiting. We may miss wr_sleepy
+ * being deasserted but hey, almost missing the train is like
+ * missing it.
+ */
+
+ if (left_to_sleep > 0) {
+ left_to_sleep =
+ wait_event_interruptible_timeout(
+ channel->wr_wait,
+ (!channel->wr_sleepy),
+ left_to_sleep);
+
+ if (!channel->wr_sleepy)
+ continue;
+
+ if (left_to_sleep
wr_mutex);
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+ if (bytes_done)
+ return bytes_done;
+ return -EINTR;
+ }
+ }
+
+desperate:
+ no_time_left = 1; /* We're out of sleeping time. Desperate! */
+
+ if (bytes_done == 0) {
+ /*
+ * Reaching here means that we allow partial return,
+ * that we've run out of time, and that we have
+ * nothing to return.
+ * So tell the FPGA to send anything it has or gets.
+ */
+
+ iowrite32(1 | (channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+ }
+
+ /*
+ * Formally speaking, we should block for data at this point.
+ * But to keep the code cleaner, we'll just finish the loop,
+ * make the unlikely check for data, and then block at the
+ * usual place.
+ */
+ }
+
+ mutex_unlock(&channel->wr_mutex);
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ return bytes_done;
+}
+
+/*
+ * The timeout argument takes values as follows:
+ * >0 : Flush with timeout
+ * ==0 : Flush, and wait idefinitely for the flush to complete
+ *
endpoint->fatal_error)
+ return -EIO;
+ rc = mutex_lock_interruptible(&channel->rd_mutex);
+
+ if (rc)
+ return rc;
+
+ /*
+ * Don't flush a closed channel. This can happen when the work queued
+ * autoflush thread fires off after the file has closed. This is not
+ * an error, just something to dismiss.
+ */
+
+ if (!channel->rd_ref_count)
+ goto done;
+
+ bufidx = channel->rd_host_buf_idx;
+
+ bufidx_minus1 = (bufidx == 0) ? channel->num_rd_buffers - 1 : bufidx-1;
+
+ end_offset_plus1 = channel->rd_host_buf_pos >>
+ channel->log2_element_size;
+
+ new_rd_host_buf_pos = channel->rd_host_buf_pos -
+ (end_offset_plus1
log2_element_size);
+
+ /* Submit the current buffer if it's nonempty */
+ if (end_offset_plus1) {
+ unsigned char *tail = channel->rd_buffers[bufidx]->addr +
+ (end_offset_plus1
log2_element_size);
+
+ /* Copy unflushed data, so we can put it in next buffer */
+ for (i = 0; i
rd_leftovers = *tail++;
+
+ spin_lock_irqsave(&channel->rd_spinlock, flags);
+
+ /* Autoflush only if a single buffer is occupied */
+
+ if ((timeout
rd_full ||
+ (bufidx_minus1 != channel->rd_fpga_buf_idx))) {
+ spin_unlock_irqrestore(&channel->rd_spinlock, flags);
+ /*
+ * A new work item may be queued by the ISR exactly
+ * now, since the execution of a work item allows the
+ * queuing of a new one while it's running.
+ */
+ goto done;
+ }
+
+ /* The 4th element is never needed for data, so it's a flag */
+ channel->rd_leftovers[3] = (new_rd_host_buf_pos != 0);
+
+ /* Set up rd_full to reflect a certain moment's state */
+
+ if (bufidx == channel->rd_fpga_buf_idx)
+ channel->rd_full = 1;
+ spin_unlock_irqrestore(&channel->rd_spinlock, flags);
+
+ if (bufidx >= (channel->num_rd_buffers - 1))
+ channel->rd_host_buf_idx = 0;
+ else
+ channel->rd_host_buf_idx++;
+
+ channel->endpoint->ephw->sync_single_for_device(
+ channel->endpoint,
+ channel->rd_buffers[bufidx]->dma_addr,
+ channel->rd_buf_size,
+ DMA_TO_DEVICE);
+
+ mutex_lock(&channel->endpoint->register_mutex);
+
+ iowrite32(end_offset_plus1 - 1,
+ &channel->endpoint->registers[fpga_buf_offset_reg]);
+ mmiowb();
+
+ iowrite32((channel->chan_num
endpoint->registers[fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+
+ mutex_unlock(&channel->endpoint->register_mutex);
+ } else if (bufidx == 0)
+ bufidx = channel->num_rd_buffers - 1;
+ else
+ bufidx--;
+
+ channel->rd_host_buf_pos = new_rd_host_buf_pos;
+
+ if (timeout
rd_host_buf_idx the one after it.
+ *
+ * If bufidx == channel->rd_fpga_buf_idx we're either empty or full.
+ */
+
+ rc = 0;
+
+ while (1) { /* Loop waiting for draining of buffers */
+ spin_lock_irqsave(&channel->rd_spinlock, flags);
+
+ if (bufidx != channel->rd_fpga_buf_idx)
+ channel->rd_full = 1; /*
+ * Not really full,
+ * but needs waiting.
+ */
+
+ empty = !channel->rd_full;
+
+ spin_unlock_irqrestore(&channel->rd_spinlock, flags);
+
+ if (empty)
+ break;
+
+ /*
+ * Indefinite sleep with mutex taken. With data waiting for
+ * flushing user should not be surprised if open() for write
+ * sleeps.
+ */
+ if (timeout == 0)
+ wait_event_interruptible(channel->rd_wait,
+ (!channel->rd_full));
+
+ else if (wait_event_interruptible_timeout(
+ channel->rd_wait,
+ (!channel->rd_full),
+ timeout) == 0) {
+ pr_warn("xillybus: "
+ "Timed out while flushing. "
+ "Output data may be lost.\n");
+
+ rc = -ETIMEDOUT;
+ break;
+ }
+
+ if (channel->rd_full) {
+ rc = -EINTR;
+ break;
+ }
+ }
+
+done:
+ mutex_unlock(&channel->rd_mutex);
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ return rc;
+}
+
+static int xillybus_flush(struct file *filp, fl_owner_t id)
+{
+ if (!(filp->f_mode & FMODE_WRITE))
+ return 0;
+
+ return xillybus_myflush(filp->private_data, HZ); /* 1 second timeout */
+}
+
+static void xillybus_autoflush(struct work_struct *work)
+{
+ struct delayed_work *workitem = container_of(
+ work, struct delayed_work, work);
+ struct xilly_channel *channel = container_of(
+ workitem, struct xilly_channel, rd_workitem);
+ int rc;
+
+ rc = xillybus_myflush(channel, -1);
+
+ if (rc == -EINTR)
+ pr_warn("xillybus: Autoflush failed because "
+ "work queue thread got a signal.\n");
+ else if (rc)
+ pr_err("xillybus: Autoflush failed under "
+ "weird circumstances.\n");
+
+}
+
+static ssize_t xillybus_write(struct file *filp, const char *userbuf,
+ size_t count, loff_t *f_pos)
+{
+ ssize_t rc;
+ unsigned long flags;
+ int bytes_done = 0;
+ struct xilly_channel *channel = filp->private_data;
+
+ int full, exhausted;
+ /* Initializations are there only to silence warnings */
+
+ int howmany = 0, bufpos = 0, bufidx = 0, bufferdone = 0;
+ int end_offset_plus1 = 0;
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ rc = mutex_lock_interruptible(&channel->rd_mutex);
+
+ if (rc)
+ return rc;
+
+ rc = 0; /* Just to be clear about it. Compiler optimizes this out */
+
+ while (1) {
+ int bytes_to_do = count - bytes_done;
+
+ spin_lock_irqsave(&channel->rd_spinlock, flags);
+
+ full = channel->rd_full;
+
+ if (!full) {
+ bufidx = channel->rd_host_buf_idx;
+ bufpos = channel->rd_host_buf_pos;
+ howmany = channel->rd_buf_size - bufpos;
+
+ /*
+ * Update rd_host_* to its state after this operation.
+ * count=0 means committing the buffer immediately,
+ * which is like flushing, but not necessarily block.
+ */
+
+ if ((howmany > bytes_to_do) &&
+ (count ||
+ ((bufpos >> channel->log2_element_size) == 0))) {
+ bufferdone = 0;
+
+ howmany = bytes_to_do;
+ channel->rd_host_buf_pos += howmany;
+ } else {
+ bufferdone = 1;
+
+ if (count) {
+ end_offset_plus1 =
+ channel->rd_buf_size >>
+ channel->log2_element_size;
+ channel->rd_host_buf_pos = 0;
+ } else {
+ unsigned char *tail;
+ int i;
+
+ end_offset_plus1 = bufpos >>
+ channel->log2_element_size;
+
+ channel->rd_host_buf_pos -=
+ end_offset_plus1
log2_element_size;
+
+ tail = channel->
+ rd_buffers[bufidx]->addr +
+ (end_offset_plus1
log2_element_size);
+
+ for (i = 0;
+ i
rd_host_buf_pos;
+ i++)
+ channel->rd_leftovers =
+ *tail++;
+ }
+
+ if (bufidx == channel->rd_fpga_buf_idx)
+ channel->rd_full = 1;
+
+ if (bufidx >= (channel->num_rd_buffers - 1))
+ channel->rd_host_buf_idx = 0;
+ else
+ channel->rd_host_buf_idx++;
+ }
+ }
+
+ /*
+ * Marking our situation after the possible changes above,
+ * for use after releasing the spinlock.
+ *
+ * full = full before change
+ * exhasted = full after possible change
+ */
+
+ exhausted = channel->rd_full;
+
+ spin_unlock_irqrestore(&channel->rd_spinlock, flags);
+
+ if (!full) { /* Go on, now without the spinlock */
+ unsigned char *head =
+ channel->rd_buffers[bufidx]->addr;
+ int i;
+
+ if ((bufpos == 0) || /* Zero means it's virgin */
+ (channel->rd_leftovers[3] != 0)) {
+ channel->endpoint->ephw->sync_single_for_cpu(
+ channel->endpoint,
+ channel->rd_buffers[bufidx]->dma_addr,
+ channel->rd_buf_size,
+ DMA_TO_DEVICE);
+
+ /* Virgin, but leftovers are due */
+ for (i = 0; i
rd_leftovers;
+
+ channel->rd_leftovers[3] = 0; /* Clear flag */
+ }
+
+ if (copy_from_user(
+ channel->rd_buffers[bufidx]->addr + bufpos,
+ userbuf, howmany))
+ rc = -EFAULT;
+
+ userbuf += howmany;
+ bytes_done += howmany;
+
+ if (bufferdone) {
+ channel->endpoint->ephw->
+ sync_single_for_device(
+ channel->endpoint,
+ channel->rd_buffers[bufidx]->
+ dma_addr,
+ channel->rd_buf_size,
+ DMA_TO_DEVICE);
+
+ mutex_lock(&channel->endpoint->register_mutex);
+
+ iowrite32(end_offset_plus1 - 1,
+ &channel->endpoint->registers[
+ fpga_buf_offset_reg]);
+ mmiowb();
+ iowrite32((channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+
+ mutex_unlock(&channel->endpoint->
+ register_mutex);
+
+ channel->rd_leftovers[3] =
+ (channel->rd_host_buf_pos != 0);
+ }
+
+ if (rc) {
+ mutex_unlock(&channel->rd_mutex);
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ if (!channel->rd_synchronous)
+ queue_delayed_work(
+ xillybus_wq,
+ &channel->rd_workitem,
+ XILLY_RX_TIMEOUT);
+
+ return rc;
+ }
+ }
+
+ if (bytes_done >= count)
+ break;
+
+ if (!exhausted)
+ continue; /* If there's more space, just go on */
+
+ if ((bytes_done > 0) && channel->rd_allow_partial)
+ break;
+
+ /*
+ * Indefinite sleep with mutex taken. With data waiting for
+ * flushing, user should not be surprised if open() for write
+ * sleeps.
+ */
+
+ if (filp->f_flags & O_NONBLOCK) {
+ bytes_done = -EAGAIN;
+ break;
+ }
+
+ wait_event_interruptible(channel->rd_wait,
+ (!channel->rd_full));
+
+ if (channel->rd_full) {
+ mutex_unlock(&channel->rd_mutex);
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ if (bytes_done)
+ return bytes_done;
+ return -EINTR;
+ }
+ }
+
+ mutex_unlock(&channel->rd_mutex);
+
+ if (!channel->rd_synchronous)
+ queue_delayed_work(xillybus_wq,
+ &channel->rd_workitem,
+ XILLY_RX_TIMEOUT);
+
+ if ((channel->rd_synchronous) && (bytes_done > 0)) {
+ rc = xillybus_myflush(filp->private_data, 0); /* No timeout */
+
+ if (rc && (rc != -EINTR))
+ return rc;
+ }
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ return bytes_done;
+}
+
+static int xillybus_open(struct inode *inode, struct file *filp)
+{
+ int rc = 0;
+ unsigned long flags;
+ int minor = iminor(inode);
+ int major = imajor(inode);
+ struct xilly_endpoint *ep_iter, *endpoint = NULL;
+ struct xilly_channel *channel;
+
+ mutex_lock(&ep_list_lock);
+
+ list_for_each_entry(ep_iter, &list_of_endpoints, ep_list) {
+ if ((ep_iter->major == major) &&
+ (minor >= ep_iter->lowest_minor) &&
+ (minor
lowest_minor +
+ ep_iter->num_channels))) {
+ endpoint = ep_iter;
+ break;
+ }
+ }
+ mutex_unlock(&ep_list_lock);
+
+ if (!endpoint) {
+ pr_err("xillybus: open() failed to find a device "
+ "for major=%d and minor=%d\n", major, minor);
+ return -ENODEV;
+ }
+
+ if (endpoint->fatal_error)
+ return -EIO;
+
+ channel = endpoint->channels[1 + minor - endpoint->lowest_minor];
+ filp->private_data = channel;
+
+
+ /*
+ * It gets complicated because:
+ * 1. We don't want to take a mutex we don't have to
+ * 2. We don't want to open one direction if the other will fail.
+ */
+
+ if ((filp->f_mode & FMODE_READ) && (!channel->num_wr_buffers))
+ return -ENODEV;
+
+ if ((filp->f_mode & FMODE_WRITE) && (!channel->num_rd_buffers))
+ return -ENODEV;
+
+ if ((filp->f_mode & FMODE_READ) && (filp->f_flags & O_NONBLOCK) &&
+ (channel->wr_synchronous || !channel->wr_allow_partial ||
+ !channel->wr_supports_nonempty)) {
+ pr_err("xillybus: open() failed: "
+ "O_NONBLOCK not allowed for read on this device\n");
+ return -ENODEV;
+ }
+
+ if ((filp->f_mode & FMODE_WRITE) && (filp->f_flags & O_NONBLOCK) &&
+ (channel->rd_synchronous || !channel->rd_allow_partial)) {
+ pr_err("xillybus: open() failed: "
+ "O_NONBLOCK not allowed for write on this device\n");
+ return -ENODEV;
+ }
+
+ /*
+ * Note: open() may block on getting mutexes despite O_NONBLOCK.
+ * This shouldn't occur normally, since multiple open of the same
+ * file descriptor is almost always prohibited anyhow
+ * (*_exclusive_open is normally set in real-life systems).
+ */
+
+ if (filp->f_mode & FMODE_READ) {
+ rc = mutex_lock_interruptible(&channel->wr_mutex);
+ if (rc)
+ return rc;
+ }
+
+ if (filp->f_mode & FMODE_WRITE) {
+ rc = mutex_lock_interruptible(&channel->rd_mutex);
+ if (rc)
+ goto unlock_wr;
+ }
+
+ if ((filp->f_mode & FMODE_READ) &&
+ (channel->wr_ref_count != 0) &&
+ (channel->wr_exclusive_open)) {
+ rc = -EBUSY;
+ goto unlock;
+ }
+
+ if ((filp->f_mode & FMODE_WRITE) &&
+ (channel->rd_ref_count != 0) &&
+ (channel->rd_exclusive_open)) {
+ rc = -EBUSY;
+ goto unlock;
+ }
+
+
+ if (filp->f_mode & FMODE_READ) {
+ if (channel->wr_ref_count == 0) { /* First open of file */
+ /* Move the host to first buffer */
+ spin_lock_irqsave(&channel->wr_spinlock, flags);
+ channel->wr_host_buf_idx = 0;
+ channel->wr_host_buf_pos = 0;
+ channel->wr_fpga_buf_idx = -1;
+ channel->wr_empty = 1;
+ channel->wr_ready = 0;
+ channel->wr_sleepy = 1;
+ channel->wr_eof = -1;
+ channel->wr_hangup = 0;
+
+ spin_unlock_irqrestore(&channel->wr_spinlock, flags);
+
+ iowrite32(1 | (channel->chan_num
wr_synchronous & 1)
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+ }
+
+ channel->wr_ref_count++;
+ }
+
+ if (filp->f_mode & FMODE_WRITE) {
+ if (channel->rd_ref_count == 0) { /* First open of file */
+ /* Move the host to first buffer */
+ spin_lock_irqsave(&channel->rd_spinlock, flags);
+ channel->rd_host_buf_idx = 0;
+ channel->rd_host_buf_pos = 0;
+ channel->rd_leftovers[3] = 0; /* No leftovers. */
+ channel->rd_fpga_buf_idx = channel->num_rd_buffers - 1;
+ channel->rd_full = 0;
+
+ spin_unlock_irqrestore(&channel->rd_spinlock, flags);
+
+ iowrite32((channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+ }
+
+ channel->rd_ref_count++;
+ }
+
+unlock:
+ if (filp->f_mode & FMODE_WRITE)
+ mutex_unlock(&channel->rd_mutex);
+unlock_wr:
+ if (filp->f_mode & FMODE_READ)
+ mutex_unlock(&channel->wr_mutex);
+
+ if (!rc && (!channel->seekable))
+ return nonseekable_open(inode, filp);
+
+ return rc;
+}
+
+static int xillybus_release(struct inode *inode, struct file *filp)
+{
+ int rc;
+ unsigned long flags;
+ struct xilly_channel *channel = filp->private_data;
+
+ int buf_idx;
+ int eof;
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ if (filp->f_mode & FMODE_WRITE) {
+ rc = mutex_lock_interruptible(&channel->rd_mutex);
+
+ if (rc) {
+ pr_warn("xillybus: Failed to close file. "
+ "Hardware left in messy state.\n");
+ return rc;
+ }
+
+ channel->rd_ref_count--;
+
+ if (channel->rd_ref_count == 0) {
+
+ /*
+ * We rely on the kernel calling flush()
+ * before we get here.
+ */
+
+ iowrite32((channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+ }
+ mutex_unlock(&channel->rd_mutex);
+ }
+
+ if (filp->f_mode & FMODE_READ) {
+ rc = mutex_lock_interruptible(&channel->wr_mutex);
+ if (rc) {
+ pr_warn("xillybus: Failed to close file. "
+ "Hardware left in messy state.\n");
+ return rc;
+ }
+
+ channel->wr_ref_count--;
+
+ if (channel->wr_ref_count == 0) {
+
+ iowrite32(1 | (channel->chan_num
endpoint->registers[
+ fpga_buf_ctrl_reg]);
+ mmiowb(); /* Just to appear safe */
+
+ /*
+ * This is crazily cautious: We make sure that not
+ * only that we got an EOF (be it because we closed
+ * the channel or because of a user's EOF), but verify
+ * that it's one beyond the last buffer arrived, so
+ * we have no leftover buffers pending before wrapping
+ * up (which can only happen in asynchronous channels,
+ * BTW)
+ */
+
+ while (1) {
+ spin_lock_irqsave(&channel->wr_spinlock,
+ flags);
+ buf_idx = channel->wr_fpga_buf_idx;
+ eof = channel->wr_eof;
+ channel->wr_sleepy = 1;
+ spin_unlock_irqrestore(&channel->wr_spinlock,
+ flags);
+
+ /*
+ * Check if eof points at the buffer after
+ * the last one the FPGA submitted. Note that
+ * no EOF is marked by negative eof.
+ */
+
+ buf_idx++;
+ if (buf_idx == channel->num_wr_buffers)
+ buf_idx = 0;
+
+ if (buf_idx == eof)
+ break;
+
+ /*
+ * Steal extra 100 ms if awaken by interrupt.
+ * This is a simple workaround for an
+ * interrupt pending when entering, which would
+ * otherwise result in declaring the hardware
+ * non-responsive.
+ */
+
+ if (wait_event_interruptible(
+ channel->wr_wait,
+ (!channel->wr_sleepy)))
+ msleep(100);
+
+ if (channel->wr_sleepy) {
+ mutex_unlock(&channel->wr_mutex);
+ pr_warn("xillybus: Hardware failed to "
+ "respond to close command, "
+ "therefore left in "
+ "messy state.\n");
+ return -EINTR;
+ }
+ }
+ }
+
+ mutex_unlock(&channel->wr_mutex);
+ }
+
+ return 0;
+}
+loff_t xillybus_llseek(struct file *filp, loff_t offset, int whence)
+{
+ struct xilly_channel *channel = filp->private_data;
+ loff_t pos = filp->f_pos;
+ int rc = 0;
+
+ /*
+ * Take both mutexes not allowing interrupts, since it seems like
+ * common applications don't expect an -EINTR here. Besides, multiple
+ * access to a single file desriptor on seekable devices is a mess
+ * anyhow.
+ */
+
+ if (channel->endpoint->fatal_error)
+ return -EIO;
+
+ mutex_lock(&channel->wr_mutex);
+ mutex_lock(&channel->rd_mutex);
+
+ switch (whence) {
+ case 0:
+ pos = offset;
+ break;
+ case 1:
+ pos += offset;
+ break;
+ case 2:
+ pos = offset; /* Going to the end => to the beginning */
+ break;
+ default: