ssize_t mei_cl_write()

in mei/client.c [1932:2086]


ssize_t mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb)
{
	struct mei_device *dev;
	struct mei_msg_data *buf;
	struct mei_msg_hdr *mei_hdr = NULL;
	size_t hdr_len;
	size_t hbuf_len, dr_len;
	size_t buf_len;
	size_t data_len;
	int hbuf_slots;
	u32 dr_slots;
	u32 dma_len;
	ssize_t rets;
	bool blocking;
	const void *data;

	if (WARN_ON(!cl || !cl->dev))
		return -ENODEV;

	if (WARN_ON(!cb))
		return -EINVAL;

	dev = cl->dev;

	buf = &cb->buf;
	buf_len = buf->size;

	cl_dbg(dev, cl, "buf_len=%zd\n", buf_len);

	blocking = cb->blocking;
	data = buf->data;

	rets = pm_runtime_get(dev->dev);
	if (rets < 0 && rets != -EINPROGRESS) {
		pm_runtime_put_noidle(dev->dev);
		cl_err(dev, cl, "rpm: get failed %zd\n", rets);
		goto free;
	}

	cb->buf_idx = 0;
	cl->writing_state = MEI_IDLE;


	rets = mei_cl_tx_flow_ctrl_creds(cl);
	if (rets < 0)
		goto err;

	mei_hdr = mei_msg_hdr_init(cb);
	if (IS_ERR(mei_hdr)) {
		rets = -PTR_ERR(mei_hdr);
		mei_hdr = NULL;
		goto err;
	}

	cl_dbg(dev, cl, "Extended Header %d vtag = %d\n",
	       mei_hdr->extended, cb->vtag);

	hdr_len = sizeof(*mei_hdr) + mei_hdr->length;

	if (rets == 0) {
		cl_dbg(dev, cl, "No flow control credentials: not sending.\n");
		rets = buf_len;
		goto out;
	}

	if (!mei_hbuf_acquire(dev)) {
		cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n");
		rets = buf_len;
		goto out;
	}

	hbuf_slots = mei_hbuf_empty_slots(dev);
	if (hbuf_slots < 0) {
		rets = -EOVERFLOW;
		goto out;
	}

	hbuf_len = mei_slots2data(hbuf_slots) & MEI_MSG_MAX_LEN_MASK;
	dr_slots = mei_dma_ring_empty_slots(dev);
	dr_len =  mei_slots2data(dr_slots);

	if (hdr_len + buf_len <= hbuf_len) {
		data_len = buf_len;
		mei_hdr->msg_complete = 1;
	} else if (dr_slots && hbuf_len >= hdr_len + sizeof(dma_len)) {
		mei_hdr->dma_ring = 1;
		if (buf_len > dr_len)
			buf_len = dr_len;
		else
			mei_hdr->msg_complete = 1;

		data_len = sizeof(dma_len);
		dma_len = buf_len;
		data = &dma_len;
	} else {
		buf_len = hbuf_len - hdr_len;
		data_len = buf_len;
	}

	mei_hdr->length += data_len;

	if (mei_hdr->dma_ring)
		mei_dma_ring_write(dev, buf->data, buf_len);
	rets = mei_write_message(dev, mei_hdr, hdr_len, data, data_len);

	if (rets)
		goto err;

	rets = mei_cl_tx_flow_ctrl_creds_reduce(cl);
	if (rets)
		goto err;

	cl->writing_state = MEI_WRITING;
	cb->buf_idx = buf_len;
	/* restore return value */
	buf_len = buf->size;

out:
	if (mei_hdr->msg_complete)
		mei_tx_cb_enqueue(cb, &dev->write_waiting_list);
	else
		mei_tx_cb_enqueue(cb, &dev->write_list);

	cb = NULL;
	if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) {

		mutex_unlock(&dev->device_lock);
		rets = wait_event_interruptible(cl->tx_wait,
				cl->writing_state == MEI_WRITE_COMPLETE ||
				(!mei_cl_is_connected(cl)));
		mutex_lock(&dev->device_lock);
		/* wait_event_interruptible returns -ERESTARTSYS */
		if (rets) {
			if (signal_pending(current))
				rets = -EINTR;
			goto err;
		}
		if (cl->writing_state != MEI_WRITE_COMPLETE) {
			rets = -EFAULT;
			goto err;
		}
	}

	rets = buf_len;
err:
	cl_dbg(dev, cl, "rpm: autosuspend\n");
	pm_runtime_mark_last_busy(dev->dev);
	pm_runtime_put_autosuspend(dev->dev);
free:
	mei_io_cb_free(cb);

	kfree(mei_hdr);

	return rets;
}