in xillybus/xillybus_core.c [1014:1175]
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;
}