static ssize_t xillybus_write()

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