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