static ssize_t xillybus_read()

in xillybus/xillybus_core.c [690:1005]


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