in xillybus/xillyusb.c [1419:1592]
static ssize_t xillyusb_read(struct file *filp, char __user *userbuf,
size_t count, loff_t *f_pos)
{
struct xillyusb_channel *chan = filp->private_data;
struct xillyusb_dev *xdev = chan->xdev;
struct xillyfifo *fifo = chan->in_fifo;
int chan_num = (chan->chan_idx << 1) | 1;
long deadline, left_to_sleep;
int bytes_done = 0;
bool sent_set_push = false;
int rc;
deadline = jiffies + 1 + XILLY_RX_TIMEOUT;
rc = mutex_lock_interruptible(&chan->in_mutex);
if (rc)
return rc;
while (1) {
u32 fifo_checkpoint_bytes, complete_checkpoint_bytes;
u32 complete_checkpoint, fifo_checkpoint;
u32 checkpoint;
s32 diff, leap;
unsigned int sh = chan->in_log2_element_size;
bool checkpoint_for_complete;
rc = fifo_read(fifo, (__force void *)userbuf + bytes_done,
count - bytes_done, xilly_copy_to_user);
if (rc < 0)
break;
bytes_done += rc;
chan->in_consumed_bytes += rc;
left_to_sleep = deadline - ((long)jiffies);
/*
* Some 32-bit arithmetic that may wrap. Note that
* complete_checkpoint is rounded up to the closest element
* boundary, because the read() can't be completed otherwise.
* fifo_checkpoint_bytes is rounded down, because it protects
* in_fifo from overflowing.
*/
fifo_checkpoint_bytes = chan->in_consumed_bytes + fifo->size;
complete_checkpoint_bytes =
chan->in_consumed_bytes + count - bytes_done;
fifo_checkpoint = fifo_checkpoint_bytes >> sh;
complete_checkpoint =
(complete_checkpoint_bytes + (1 << sh) - 1) >> sh;
diff = (fifo_checkpoint - complete_checkpoint) << sh;
if (chan->in_synchronous && diff >= 0) {
checkpoint = complete_checkpoint;
checkpoint_for_complete = true;
} else {
checkpoint = fifo_checkpoint;
checkpoint_for_complete = false;
}
leap = (checkpoint - chan->in_current_checkpoint) << sh;
/*
* To prevent flooding of OPCODE_SET_CHECKPOINT commands as
* data is consumed, it's issued only if it moves the
* checkpoint by at least an 8th of the FIFO's size, or if
* it's necessary to complete the number of bytes requested by
* the read() call.
*
* chan->read_data_ok is checked to spare an unnecessary
* submission after receiving EOF, however it's harmless if
* such slips away.
*/
if (chan->read_data_ok &&
(leap > (fifo->size >> 3) ||
(checkpoint_for_complete && leap > 0))) {
chan->in_current_checkpoint = checkpoint;
rc = xillyusb_send_opcode(xdev, chan_num,
OPCODE_SET_CHECKPOINT,
checkpoint);
if (rc)
break;
}
if (bytes_done == count ||
(left_to_sleep <= 0 && bytes_done))
break;
/*
* Reaching here means that the FIFO was empty when
* fifo_read() returned, but not necessarily right now. Error
* and EOF are checked and reported only now, so that no data
* that managed its way to the FIFO is lost.
*/
if (!READ_ONCE(chan->read_data_ok)) { /* FPGA has sent EOF */
/* Has data slipped into the FIFO since fifo_read()? */
smp_rmb();
if (READ_ONCE(fifo->fill))
continue;
rc = 0;
break;
}
if (xdev->error) {
rc = xdev->error;
break;
}
if (filp->f_flags & O_NONBLOCK) {
rc = -EAGAIN;
break;
}
if (!sent_set_push) {
rc = xillyusb_send_opcode(xdev, chan_num,
OPCODE_SET_PUSH,
complete_checkpoint);
if (rc)
break;
sent_set_push = true;
}
if (left_to_sleep > 0) {
/*
* Note that when xdev->error is set (e.g. when the
* device is unplugged), read_data_ok turns zero and
* fifo->waitq is awaken.
* Therefore no special attention to xdev->error.
*/
rc = wait_event_interruptible_timeout
(fifo->waitq,
fifo->fill || !chan->read_data_ok,
left_to_sleep);
} else { /* bytes_done == 0 */
/* Tell FPGA to send anything it has */
rc = request_read_anything(chan, OPCODE_UPDATE_PUSH);
if (rc)
break;
rc = wait_event_interruptible
(fifo->waitq,
fifo->fill || !chan->read_data_ok);
}
if (rc < 0) {
rc = -EINTR;
break;
}
}
if (((filp->f_flags & O_NONBLOCK) || chan->poll_used) &&
!READ_ONCE(fifo->fill))
request_read_anything(chan, OPCODE_SET_PUSH);
mutex_unlock(&chan->in_mutex);
if (bytes_done)
return bytes_done;
return rc;
}