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