static int __qcom_smd_send()

in qcom_smd.c [743:810]


static int __qcom_smd_send(struct qcom_smd_channel *channel, const void *data,
			   int len, bool wait)
{
	__le32 hdr[5] = { cpu_to_le32(len), };
	int tlen = sizeof(hdr) + len;
	unsigned long flags;
	int ret;

	/* Word aligned channels only accept word size aligned data */
	if (channel->info_word && len % 4)
		return -EINVAL;

	/* Reject packets that are too big */
	if (tlen >= channel->fifo_size)
		return -EINVAL;

	/* Highlight the fact that if we enter the loop below we might sleep */
	if (wait)
		might_sleep();

	spin_lock_irqsave(&channel->tx_lock, flags);

	while (qcom_smd_get_tx_avail(channel) < tlen &&
	       channel->state == SMD_CHANNEL_OPENED) {
		if (!wait) {
			ret = -EAGAIN;
			goto out_unlock;
		}

		SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 0);

		/* Wait without holding the tx_lock */
		spin_unlock_irqrestore(&channel->tx_lock, flags);

		ret = wait_event_interruptible(channel->fblockread_event,
				       qcom_smd_get_tx_avail(channel) >= tlen ||
				       channel->state != SMD_CHANNEL_OPENED);
		if (ret)
			return ret;

		spin_lock_irqsave(&channel->tx_lock, flags);

		SET_TX_CHANNEL_FLAG(channel, fBLOCKREADINTR, 1);
	}

	/* Fail if the channel was closed */
	if (channel->state != SMD_CHANNEL_OPENED) {
		ret = -EPIPE;
		goto out_unlock;
	}

	SET_TX_CHANNEL_FLAG(channel, fTAIL, 0);

	qcom_smd_write_fifo(channel, hdr, sizeof(hdr));
	qcom_smd_write_fifo(channel, data, len);

	SET_TX_CHANNEL_FLAG(channel, fHEAD, 1);

	/* Ensure ordering of channel info updates */
	wmb();

	qcom_smd_signal_channel(channel);

out_unlock:
	spin_unlock_irqrestore(&channel->tx_lock, flags);

	return ret;
}