root/drivers/char/xillybus/xillybus_core.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * 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.
 */

#include <linux/list.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/crc32.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include "xillybus.h"
#include "xillybus_class.h"

MODULE_DESCRIPTION("Xillybus core functions");
MODULE_AUTHOR("Eli Billauer, Xillybus Ltd.");
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              0x0008
#define fpga_dma_control_reg           0x0020
#define fpga_dma_bufno_reg             0x0024
#define fpga_dma_bufaddr_lowaddr_reg   0x0028
#define fpga_dma_bufaddr_highaddr_reg  0x002c
#define fpga_buf_ctrl_reg              0x0030
#define fpga_buf_offset_reg            0x0034
#define fpga_endian_reg                0x0040

#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 const char xillyname[] = "xillybus";

static 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(struct xilly_endpoint *endpoint, 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;

        dev_warn(endpoint->dev,
                 "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;

        buf = ep->msgbuf_addr;
        buf_size = ep->msg_buf_size/sizeof(u32);

        dma_sync_single_for_cpu(ep->dev, ep->msgbuf_dma_addr,
                                ep->msg_buf_size, DMA_FROM_DEVICE);

        for (i = 0; i < buf_size; i += 2) {
                if (((buf[i+1] >> 28) & 0xf) != ep->msg_counter) {
                        malformed_message(ep, &buf[i]);
                        dev_warn(ep->dev,
                                 "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) {
                                dev_err(ep->dev,
                                        "Lost sync with interrupt messages. Stopping.\n");
                        } else {
                                dma_sync_single_for_device(ep->dev,
                                                           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[i] & (1 << 22)) /* Last message */
                        break;
        }

        if (i >= buf_size) {
                dev_err(ep->dev, "Bad interrupt message. Stopping.\n");
                return IRQ_HANDLED;
        }

        buf_size = i + 2;

        for (i = 0; i < buf_size; i += 2) { /* Scan through messages */
                opcode = (buf[i] >> 24) & 0xff;

                msg_dir = buf[i] & 1;
                msg_channel = (buf[i] >> 1) & 0x7ff;
                msg_bufno = (buf[i] >> 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(ep, &buf[i]);
                                break;
                        }

                        channel = ep->channels[msg_channel];

                        if (msg_dir) { /* Write channel */
                                if (msg_bufno >= channel->num_wr_buffers) {
                                        malformed_message(ep, &buf[i]);
                                        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(ep, &buf[i]);
                                        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(ep, &buf[i]);
                                break;
                        }

                        channel = ep->channels[msg_channel];

                        if (msg_bufno >= channel->num_wr_buffers) {
                                malformed_message(ep, &buf[i]);
                                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:
                        if ((msg_channel > ep->num_channels) ||
                            (msg_channel == 0) || (!msg_dir) ||
                            !ep->channels[msg_channel]->num_wr_buffers) {
                                malformed_message(ep, &buf[i]);
                                break;
                        }
                        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() */
                        dev_err(ep->dev,
                                "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(ep, &buf[i]);
                        break;
                }
        }

        dma_sync_single_for_device(ep->dev, 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!
 */

static void xillybus_autoflush(struct work_struct *work);

struct xilly_alloc_state {
        void *salami;
        int left_of_salami;
        int nbuffer;
        enum dma_data_direction direction;
        u32 regdirection;
};

static void xilly_unmap(void *ptr)
{
        struct xilly_mapping *data = ptr;

        dma_unmap_single(data->device, data->dma_addr,
                         data->size, data->direction);

        kfree(ptr);
}

static int xilly_map_single(struct xilly_endpoint *ep,
                            void *ptr,
                            size_t size,
                            int direction,
                            dma_addr_t *ret_dma_handle
        )
{
        dma_addr_t addr;
        struct xilly_mapping *this;

        this = kzalloc_obj(*this);
        if (!this)
                return -ENOMEM;

        addr = dma_map_single(ep->dev, ptr, size, direction);

        if (dma_mapping_error(ep->dev, addr)) {
                kfree(this);
                return -ENODEV;
        }

        this->device = ep->dev;
        this->dma_addr = addr;
        this->size = size;
        this->direction = direction;

        *ret_dma_handle = addr;

        return devm_add_action_or_reset(ep->dev, xilly_unmap, this);
}

static int xilly_get_dma_buffers(struct xilly_endpoint *ep,
                                 struct xilly_alloc_state *s,
                                 struct xilly_buffer **buffers,
                                 int bufnum, int bytebufsize)
{
        int i, rc;
        dma_addr_t dma_addr;
        struct device *dev = ep->dev;
        struct xilly_buffer *this_buffer = NULL; /* Init to silence warning */

        if (buffers) { /* Not the message buffer */
                this_buffer = devm_kcalloc(dev, bufnum,
                                           sizeof(struct xilly_buffer),
                                           GFP_KERNEL);
                if (!this_buffer)
                        return -ENOMEM;
        }

        for (i = 0; i < bufnum; i++) {
                /*
                 * Buffers are expected in descending size order, so there
                 * is either enough space for this buffer or none at all.
                 */

                if ((s->left_of_salami < bytebufsize) &&
                    (s->left_of_salami > 0)) {
                        dev_err(ep->dev,
                                "Corrupt buffer allocation in IDT. Aborting.\n");
                        return -ENODEV;
                }

                if (s->left_of_salami == 0) {
                        int allocorder, allocsize;

                        allocsize = PAGE_SIZE;
                        allocorder = 0;
                        while (bytebufsize > allocsize) {
                                allocsize *= 2;
                                allocorder++;
                        }

                        s->salami = (void *) devm_get_free_pages(
                                dev,
                                GFP_KERNEL | __GFP_DMA32 | __GFP_ZERO,
                                allocorder);
                        if (!s->salami)
                                return -ENOMEM;

                        s->left_of_salami = allocsize;
                }

                rc = xilly_map_single(ep, s->salami,
                                      bytebufsize, s->direction,
                                      &dma_addr);
                if (rc)
                        return rc;

                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);

                if (buffers) { /* Not the message buffer */
                        this_buffer->addr = s->salami;
                        this_buffer->dma_addr = dma_addr;
                        buffers[i] = this_buffer++;

                        iowrite32(s->regdirection | s->nbuffer++,
                                  ep->registers + fpga_dma_bufno_reg);
                } else {
                        ep->msgbuf_addr = s->salami;
                        ep->msgbuf_dma_addr = dma_addr;
                        ep->msg_buf_size = bytebufsize;

                        iowrite32(s->regdirection,
                                  ep->registers + fpga_dma_bufno_reg);
                }

                s->left_of_salami -= bytebufsize;
                s->salami += bytebufsize;
        }
        return 0;
}

static int xilly_setupchannels(struct xilly_endpoint *ep,
                               unsigned char *chandesc,
                               int entries)
{
        struct device *dev = ep->dev;
        int i, entry, rc;
        struct xilly_channel *channel;
        int channelnum, bufnum, bufsize, format, is_writebuf;
        int bytebufsize;
        int synchronous, allowpartial, exclusive_open, seekable;
        int supports_nonempty;
        int msg_buf_done = 0;

        struct xilly_alloc_state rd_alloc = {
                .salami = NULL,
                .left_of_salami = 0,
                .nbuffer = 1,
                .direction = DMA_TO_DEVICE,
                .regdirection = 0,
        };

        struct xilly_alloc_state wr_alloc = {
                .salami = NULL,
                .left_of_salami = 0,
                .nbuffer = 1,
                .direction = DMA_FROM_DEVICE,
                .regdirection = 0x80000000,
        };

        channel = devm_kcalloc(dev, ep->num_channels,
                               sizeof(struct xilly_channel), GFP_KERNEL);
        if (!channel)
                return -ENOMEM;

        ep->channels = devm_kcalloc(dev, ep->num_channels + 1,
                                    sizeof(struct xilly_channel *),
                                    GFP_KERNEL);
        if (!ep->channels)
                return -ENOMEM;

        ep->channels[0] = NULL; /* Channel 0 is message buf. */

        /* Initialize all channels with defaults */

        for (i = 1; i <= ep->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[i] = channel++;
        }

        for (entry = 0; entry < entries; entry++, chandesc += 4) {
                struct xilly_buffer **buffers = NULL;

                is_writebuf = chandesc[0] & 0x01;
                channelnum = (chandesc[0] >> 1) | ((chandesc[1] & 0x0f) << 7);
                format = (chandesc[1] >> 4) & 0x03;
                allowpartial = (chandesc[1] >> 6) & 0x01;
                synchronous = (chandesc[1] >> 7) & 0x01;
                bufsize = 1 << (chandesc[2] & 0x1f);
                bufnum = 1 << (chandesc[3] & 0x0f);
                exclusive_open = (chandesc[2] >> 7) & 0x01;
                seekable = (chandesc[2] >> 6) & 0x01;
                supports_nonempty = (chandesc[2] >> 5) & 0x01;

                if ((channelnum > ep->num_channels) ||
                    ((channelnum == 0) && !is_writebuf)) {
                        dev_err(ep->dev,
                                "IDT requests channel out of range. Aborting.\n");
                        return -ENODEV;
                }

                channel = ep->channels[channelnum]; /* NULL for msg channel */

                if (!is_writebuf || channelnum > 0) {
                        channel->log2_element_size = ((format > 2) ?
                                                      2 : format);

                        bytebufsize = bufsize *
                                (1 << channel->log2_element_size);

                        buffers = devm_kcalloc(dev, bufnum,
                                               sizeof(struct xilly_buffer *),
                                               GFP_KERNEL);
                        if (!buffers)
                                return -ENOMEM;
                } else {
                        bytebufsize = bufsize << 2;
                }

                if (!is_writebuf) {
                        channel->num_rd_buffers = bufnum;
                        channel->rd_buf_size = bytebufsize;
                        channel->rd_allow_partial = allowpartial;
                        channel->rd_synchronous = synchronous;
                        channel->rd_exclusive_open = exclusive_open;
                        channel->seekable = seekable;

                        channel->rd_buffers = buffers;
                        rc = xilly_get_dma_buffers(ep, &rd_alloc, buffers,
                                                   bufnum, bytebufsize);
                } else if (channelnum > 0) {
                        channel->num_wr_buffers = bufnum;
                        channel->wr_buf_size = bytebufsize;

                        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 = buffers;
                        rc = xilly_get_dma_buffers(ep, &wr_alloc, buffers,
                                                   bufnum, bytebufsize);
                } else {
                        rc = xilly_get_dma_buffers(ep, &wr_alloc, NULL,
                                                   bufnum, bytebufsize);
                        msg_buf_done++;
                }

                if (rc)
                        return -ENOMEM;
        }

        if (!msg_buf_done) {
                dev_err(ep->dev,
                        "Corrupt IDT: No message buffer. Aborting.\n");
                return -ENODEV;
        }
        return 0;
}

static int 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 + 1;
        idt_handle->names = scan;

        while ((scan <= end_of_idt) && *scan) {
                while ((scan <= end_of_idt) && *scan++)
                        /* Do nothing, just scan thru string */;
                count++;
        }

        idt_handle->names_len = scan - idt_handle->names;

        scan++;

        if (scan > end_of_idt) {
                dev_err(endpoint->dev,
                        "IDT device name list overflow. Aborting.\n");
                return -ENODEV;
        }
        idt_handle->chandesc = scan;

        len = endpoint->idtlen - (3 + ((int) (scan - idt)));

        if (len & 0x03) {
                dev_err(endpoint->dev,
                        "Corrupt IDT device name list. Aborting.\n");
                return -ENODEV;
        }

        idt_handle->entries = len >> 2;
        endpoint->num_channels = count;

        return 0;
}

static int xilly_obtain_idt(struct xilly_endpoint *endpoint)
{
        struct xilly_channel *channel;
        unsigned char *version;
        long t;

        channel = endpoint->channels[1]; /* This should be generated ad-hoc */

        channel->wr_sleepy = 1;

        iowrite32(1 |
                  (3 << 24), /* Opcode 3 for channel 0 = Send IDT */
                  endpoint->registers + fpga_buf_ctrl_reg);

        t = wait_event_interruptible_timeout(channel->wr_wait,
                                             (!channel->wr_sleepy),
                                             XILLY_TIMEOUT);

        if (t <= 0) {
                dev_err(endpoint->dev, "Failed to obtain IDT. Aborting.\n");

                if (endpoint->fatal_error)
                        return -EIO;

                return -ENODEV;
        }

        dma_sync_single_for_cpu(channel->endpoint->dev,
                                channel->wr_buffers[0]->dma_addr,
                                channel->wr_buf_size,
                                DMA_FROM_DEVICE);

        if (channel->wr_buffers[0]->end_offset != endpoint->idtlen) {
                dev_err(endpoint->dev,
                        "IDT length mismatch (%d != %d). Aborting.\n",
                        channel->wr_buffers[0]->end_offset, endpoint->idtlen);
                return -ENODEV;
        }

        if (crc32_le(~0, channel->wr_buffers[0]->addr,
                     endpoint->idtlen+1) != 0) {
                dev_err(endpoint->dev, "IDT failed CRC check. Aborting.\n");
                return -ENODEV;
        }

        version = channel->wr_buffers[0]->addr;

        /* Check version number. Reject anything above 0x82. */
        if (*version > 0x82) {
                dev_err(endpoint->dev,
                        "No support for IDT version 0x%02x. Maybe the xillybus driver needs an upgrade. Aborting.\n",
                        *version);
                return -ENODEV;
        }

        return 0;
}

static ssize_t xillybus_read(struct file *filp, char __user *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;

        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) << channel->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 */
                                dma_sync_single_for_cpu(channel->endpoint->dev,
                                                        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) {
                                dma_sync_single_for_device(channel->endpoint->dev,
                                                           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 << 1) |
                                          (bufidx << 12),
                                          channel->endpoint->registers +
                                          fpga_buf_ctrl_reg);
                        }

                        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;

                        rc = -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 < ( ... )" rather than "<=" excludes
                         * requesting a full buffer, which would obviously
                         * cause a buffer transmission anyhow
                         */

                        if (channel->wr_synchronous ||
                            (offsetlimit < (buf_elements - 1))) {
                                mutex_lock(&channel->endpoint->register_mutex);

                                iowrite32(offsetlimit,
                                          channel->endpoint->registers +
                                          fpga_buf_offset_reg);

                                iowrite32(1 | (channel->chan_num << 1) |
                                          (2 << 24) |  /* 2 = offset limit */
                                          (waiting_bufidx << 12),
                                          channel->endpoint->registers +
                                          fpga_buf_ctrl_reg);

                                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 (left_to_sleep > 0) /* wr_sleepy deasserted */
                                continue;

                        if (left_to_sleep < 0) { /* Interrupt */
                                mutex_unlock(&channel->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 << 1) |
                                  (3 << 24) |  /* Opcode 3, flush it all! */
                                  (waiting_bufidx << 12),
                                  channel->endpoint->registers +
                                  fpga_buf_ctrl_reg);
                }

                /*
                 * Reaching here means that we *do* have data in the buffer,
                 * but the "partial" flag disallows returning less than
                 * required. And we don't have as much. So loop again,
                 * which is likely to end up blocking indefinitely until
                 * enough data has arrived.
                 */
        }

        mutex_unlock(&channel->wr_mutex);

        if (channel->endpoint->fatal_error)
                return -EIO;

        if (rc)
                return rc;

        return bytes_done;
}

/*
 * The timeout argument takes values as follows:
 *  >0 : Flush with timeout
 * ==0 : Flush, and wait idefinitely for the flush to complete
 *  <0 : Autoflush: Flush only if there's a single buffer occupied
 */

static int xillybus_myflush(struct xilly_channel *channel, long timeout)
{
        int rc;
        unsigned long flags;

        int end_offset_plus1;
        int bufidx, bufidx_minus1;
        int i;
        int empty;
        int new_rd_host_buf_pos;

        if (channel->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 << channel->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 << channel->log2_element_size);

                /* Copy  unflushed data, so we can put it in next buffer */
                for (i = 0; i < new_rd_host_buf_pos; i++)
                        channel->rd_leftovers[i] = *tail++;

                spin_lock_irqsave(&channel->rd_spinlock, flags);

                /* Autoflush only if a single buffer is occupied */

                if ((timeout < 0) &&
                    (channel->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++;

                dma_sync_single_for_device(channel->endpoint->dev,
                                           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);

                iowrite32((channel->chan_num << 1) | /* Channel ID */
                          (2 << 24) |  /* Opcode 2, submit buffer */
                          (bufidx << 12),
                          channel->endpoint->registers + fpga_buf_ctrl_reg);

                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 < 0)
                goto done; /* Autoflush */

        /*
         * bufidx is now the last buffer written to (or equal to
         * rd_fpga_buf_idx if buffer was never written to), and
         * channel->rd_host_buf_idx the one after it.
         *
         * If bufidx == channel->rd_fpga_buf_idx we're either empty or full.
         */

        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) {
                        dev_warn(channel->endpoint->dev,
                                 "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 = to_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)
                dev_warn(channel->endpoint->dev,
                         "Autoflush failed because work queue thread got a signal.\n");
        else if (rc)
                dev_err(channel->endpoint->dev,
                        "Autoflush failed under weird circumstances.\n");
}

static ssize_t xillybus_write(struct file *filp, const char __user *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;

        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;

                                        howmany = 0;

                                        end_offset_plus1 = bufpos >>
                                                channel->log2_element_size;

                                        channel->rd_host_buf_pos -=
                                                end_offset_plus1 <<
                                                channel->log2_element_size;

                                        tail = channel->
                                                rd_buffers[bufidx]->addr +
                                                (end_offset_plus1 <<
                                                 channel->log2_element_size);

                                        for (i = 0;
                                             i < channel->rd_host_buf_pos;
                                             i++)
                                                channel->rd_leftovers[i] =
                                                        *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)) {
                                dma_sync_single_for_cpu(channel->endpoint->dev,
                                                        channel->rd_buffers[bufidx]->dma_addr,
                                                        channel->rd_buf_size,
                                                        DMA_TO_DEVICE);

                                /* Virgin, but leftovers are due */
                                for (i = 0; i < bufpos; i++)
                                        *head++ = channel->rd_leftovers[i];

                                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) {
                                dma_sync_single_for_device(channel->endpoint->dev,
                                                           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);

                                iowrite32((channel->chan_num << 1) |
                                          (2 << 24) |  /* 2 = submit buffer */
                                          (bufidx << 12),
                                          channel->endpoint->registers +
                                          fpga_buf_ctrl_reg);

                                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) {
                        rc = -EAGAIN;
                        break;
                }

                if (wait_event_interruptible(channel->rd_wait,
                                             (!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->endpoint->fatal_error)
                return -EIO;

        if (rc)
                return rc;

        if ((channel->rd_synchronous) && (bytes_done > 0)) {
                rc = xillybus_myflush(filp->private_data, 0); /* No timeout */

                if (rc && (rc != -EINTR))
                        return rc;
        }

        return bytes_done;
}

static int xillybus_open(struct inode *inode, struct file *filp)
{
        int rc;
        unsigned long flags;
        struct xilly_endpoint *endpoint;
        struct xilly_channel *channel;
        int index;

        rc = xillybus_find_inode(inode, (void **)&endpoint, &index);
        if (rc)
                return rc;

        if (endpoint->fatal_error)
                return -EIO;

        channel = endpoint->channels[1 + index];
        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)) {
                dev_err(endpoint->dev,
                        "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)) {
                dev_err(endpoint->dev,
                        "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 << 1) |
                                  (4 << 24) |  /* Opcode 4, open channel */
                                  ((channel->wr_synchronous & 1) << 23),
                                  channel->endpoint->registers +
                                  fpga_buf_ctrl_reg);
                }

                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 << 1) |
                                  (4 << 24),   /* Opcode 4, open channel */
                                  channel->endpoint->registers +
                                  fpga_buf_ctrl_reg);
                }

                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)
{
        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) {
                mutex_lock(&channel->rd_mutex);

                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 << 1) | /* Channel ID */
                                  (5 << 24),  /* Opcode 5, close channel */
                                  channel->endpoint->registers +
                                  fpga_buf_ctrl_reg);
                }
                mutex_unlock(&channel->rd_mutex);
        }

        if (filp->f_mode & FMODE_READ) {
                mutex_lock(&channel->wr_mutex);

                channel->wr_ref_count--;

                if (channel->wr_ref_count == 0) {
                        iowrite32(1 | (channel->chan_num << 1) |
                                  (5 << 24),  /* Opcode 5, close channel */
                                  channel->endpoint->registers +
                                  fpga_buf_ctrl_reg);

                        /*
                         * 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);
                                        dev_warn(channel->endpoint->dev,
                                                 "Hardware failed to respond to close command, therefore left in messy state.\n");
                                        return -EINTR;
                                }
                        }
                }

                mutex_unlock(&channel->wr_mutex);
        }

        return 0;
}

static 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 descriptor 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 SEEK_SET:
                pos = offset;
                break;
        case SEEK_CUR:
                pos += offset;
                break;
        case SEEK_END:
                pos = offset; /* Going to the end => to the beginning */
                break;
        default:
                rc = -EINVAL;
                goto end;
        }

        /* In any case, we must finish on an element boundary */
        if (pos & ((1 << channel->log2_element_size) - 1)) {
                rc = -EINVAL;
                goto end;
        }

        mutex_lock(&channel->endpoint->register_mutex);

        iowrite32(pos >> channel->log2_element_size,
                  channel->endpoint->registers + fpga_buf_offset_reg);

        iowrite32((channel->chan_num << 1) |
                  (6 << 24),  /* Opcode 6, set address */
                  channel->endpoint->registers + fpga_buf_ctrl_reg);

        mutex_unlock(&channel->endpoint->register_mutex);

end:
        mutex_unlock(&channel->rd_mutex);
        mutex_unlock(&channel->wr_mutex);

        if (rc) /* Return error after releasing mutexes */
                return rc;

        filp->f_pos = pos;

        /*
         * Since seekable devices are allowed only when the channel is
         * synchronous, we assume that there is no data pending in either
         * direction (which holds true as long as no concurrent access on the
         * file descriptor takes place).
         * The only thing we may need to throw away is leftovers from partial
         * write() flush.
         */

        channel->rd_leftovers[3] = 0;

        return pos;
}

static __poll_t xillybus_poll(struct file *filp, poll_table *wait)
{
        struct xilly_channel *channel = filp->private_data;
        __poll_t mask = 0;
        unsigned long flags;

        poll_wait(filp, &channel->endpoint->ep_wait, wait);

        /*
         * poll() won't play ball regarding read() channels which
         * aren't asynchronous and support the nonempty message. Allowing
         * that will create situations where data has been delivered at
         * the FPGA, and users expecting select() to wake up, which it may
         * not.
         */

        if (!channel->wr_synchronous && channel->wr_supports_nonempty) {
                poll_wait(filp, &channel->wr_wait, wait);
                poll_wait(filp, &channel->wr_ready_wait, wait);

                spin_lock_irqsave(&channel->wr_spinlock, flags);
                if (!channel->wr_empty || channel->wr_ready)
                        mask |= EPOLLIN | EPOLLRDNORM;

                if (channel->wr_hangup)
                        /*
                         * Not EPOLLHUP, because its behavior is in the
                         * mist, and EPOLLIN does what we want: Wake up
                         * the read file descriptor so it sees EOF.
                         */
                        mask |=  EPOLLIN | EPOLLRDNORM;
                spin_unlock_irqrestore(&channel->wr_spinlock, flags);
        }

        /*
         * If partial data write is disallowed on a write() channel,
         * it's pointless to ever signal OK to write, because is could
         * block despite some space being available.
         */

        if (channel->rd_allow_partial) {
                poll_wait(filp, &channel->rd_wait, wait);

                spin_lock_irqsave(&channel->rd_spinlock, flags);
                if (!channel->rd_full)
                        mask |= EPOLLOUT | EPOLLWRNORM;
                spin_unlock_irqrestore(&channel->rd_spinlock, flags);
        }

        if (channel->endpoint->fatal_error)
                mask |= EPOLLERR;

        return mask;
}

static const struct file_operations xillybus_fops = {
        .owner      = THIS_MODULE,
        .read       = xillybus_read,
        .write      = xillybus_write,
        .open       = xillybus_open,
        .flush      = xillybus_flush,
        .release    = xillybus_release,
        .llseek     = xillybus_llseek,
        .poll       = xillybus_poll,
};

struct xilly_endpoint *xillybus_init_endpoint(struct device *dev)
{
        struct xilly_endpoint *endpoint;

        endpoint = devm_kzalloc(dev, sizeof(*endpoint), GFP_KERNEL);
        if (!endpoint)
                return NULL;

        endpoint->dev = dev;
        endpoint->msg_counter = 0x0b;
        endpoint->failed_messages = 0;
        endpoint->fatal_error = 0;

        init_waitqueue_head(&endpoint->ep_wait);
        mutex_init(&endpoint->register_mutex);

        return endpoint;
}
EXPORT_SYMBOL(xillybus_init_endpoint);

static int xilly_quiesce(struct xilly_endpoint *endpoint)
{
        long t;

        endpoint->idtlen = -1;

        iowrite32((u32) (endpoint->dma_using_dac & 0x0001),
                  endpoint->registers + fpga_dma_control_reg);

        t = wait_event_interruptible_timeout(endpoint->ep_wait,
                                             (endpoint->idtlen >= 0),
                                             XILLY_TIMEOUT);
        if (t <= 0) {
                dev_err(endpoint->dev,
                        "Failed to quiesce the device on exit.\n");
                return -ENODEV;
        }
        return 0;
}

int xillybus_endpoint_discovery(struct xilly_endpoint *endpoint)
{
        int rc;
        long t;

        void *bootstrap_resources;
        int idtbuffersize = (1 << PAGE_SHIFT);
        struct device *dev = endpoint->dev;

        /*
         * The bogus IDT is used during bootstrap for allocating the initial
         * message buffer, and then the message buffer and space for the IDT
         * itself. The initial message buffer is of a single page's size, but
         * it's soon replaced with a more modest one (and memory is freed).
         */

        unsigned char bogus_idt[8] = { 1, 224, (PAGE_SHIFT)-2, 0,
                                       3, 192, PAGE_SHIFT, 0 };
        struct xilly_idt_handle idt_handle;

        /*
         * Writing the value 0x00000001 to Endianness register signals which
         * endianness this processor is using, so the FPGA can swap words as
         * necessary.
         */

        iowrite32(1, endpoint->registers + fpga_endian_reg);

        /* Bootstrap phase I: Allocate temporary message buffer */

        bootstrap_resources = devres_open_group(dev, NULL, GFP_KERNEL);
        if (!bootstrap_resources)
                return -ENOMEM;

        endpoint->num_channels = 0;

        rc = xilly_setupchannels(endpoint, bogus_idt, 1);
        if (rc)
                return rc;

        /* Clear the message subsystem (and counter in particular) */
        iowrite32(0x04, endpoint->registers + fpga_msg_ctrl_reg);

        endpoint->idtlen = -1;

        /*
         * Set DMA 32/64 bit mode, quiesce the device (?!) and get IDT
         * buffer size.
         */
        iowrite32((u32) (endpoint->dma_using_dac & 0x0001),
                  endpoint->registers + fpga_dma_control_reg);

        t = wait_event_interruptible_timeout(endpoint->ep_wait,
                                             (endpoint->idtlen >= 0),
                                             XILLY_TIMEOUT);
        if (t <= 0) {
                dev_err(endpoint->dev, "No response from FPGA. Aborting.\n");
                return -ENODEV;
        }

        /* Enable DMA */
        iowrite32((u32) (0x0002 | (endpoint->dma_using_dac & 0x0001)),
                  endpoint->registers + fpga_dma_control_reg);

        /* Bootstrap phase II: Allocate buffer for IDT and obtain it */
        while (endpoint->idtlen >= idtbuffersize) {
                idtbuffersize *= 2;
                bogus_idt[6]++;
        }

        endpoint->num_channels = 1;

        rc = xilly_setupchannels(endpoint, bogus_idt, 2);
        if (rc)
                goto failed_idt;

        rc = xilly_obtain_idt(endpoint);
        if (rc)
                goto failed_idt;

        rc = xilly_scan_idt(endpoint, &idt_handle);
        if (rc)
                goto failed_idt;

        devres_close_group(dev, bootstrap_resources);

        /* Bootstrap phase III: Allocate buffers according to IDT */

        rc = xilly_setupchannels(endpoint,
                                 idt_handle.chandesc,
                                 idt_handle.entries);
        if (rc)
                goto failed_idt;

        rc = xillybus_init_chrdev(dev, &xillybus_fops,
                                  endpoint->owner, endpoint,
                                  idt_handle.names,
                                  idt_handle.names_len,
                                  endpoint->num_channels,
                                  xillyname, false);

        if (rc)
                goto failed_idt;

        devres_release_group(dev, bootstrap_resources);

        return 0;

failed_idt:
        xilly_quiesce(endpoint);
        flush_workqueue(xillybus_wq);

        return rc;
}
EXPORT_SYMBOL(xillybus_endpoint_discovery);

void xillybus_endpoint_remove(struct xilly_endpoint *endpoint)
{
        xillybus_cleanup_chrdev(endpoint, endpoint->dev);

        xilly_quiesce(endpoint);

        /*
         * Flushing is done upon endpoint release to prevent access to memory
         * just about to be released. This makes the quiesce complete.
         */
        flush_workqueue(xillybus_wq);
}
EXPORT_SYMBOL(xillybus_endpoint_remove);

static int __init xillybus_init(void)
{
        xillybus_wq = alloc_workqueue(xillyname, WQ_UNBOUND, 0);
        if (!xillybus_wq)
                return -ENOMEM;

        return 0;
}

static void __exit xillybus_exit(void)
{
        /* flush_workqueue() was called for each endpoint released */
        destroy_workqueue(xillybus_wq);
}

module_init(xillybus_init);
module_exit(xillybus_exit);