2013-05-17

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:

Show more