Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  xillybus_core.c   Sprache: 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(sizeof(*this), GFP_KERNEL);
 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, 0, 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);

Messung V0.5 in Prozent
C=94 H=93 G=93

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.30Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-28) ¤

*Eine klare Vorstellung vom Zielzustand






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126864
#Domains=1897691