/** * rpmsg_send_offchannel_raw() - send a message across to the remote processor * @rpdev: the rpmsg channel * @src: source address * @dst: destination address * @data: payload of message * @len: length of payload * @wait: indicates whether caller should block in case no TX buffers available * * This function is the base implementation for all of the rpmsg sending API. * * It will send @data of length @len to @dst, and say it's from @src. The * message will be sent to the remote processor which the @rpdev channel * belongs to. * * The message is sent using one of the TX buffers that are available for * communication with this remote processor. * * If @wait is true, the caller will be blocked until either a TX buffer is * available, or 15 seconds elapses (we don't want callers to * sleep indefinitely due to misbehaving remote processors), and in that * case -ERESTARTSYS is returned. The number '15' itself was picked * arbitrarily; there's little point in asking drivers to provide a timeout * value themselves. * * Otherwise, if @wait is false, and there are no TX buffers available, * the function will immediately fail, and -ENOMEM will be returned. * * Normally drivers shouldn't use this function directly; instead, drivers * should use the appropriate rpmsg_{try}send{to, _offchannel} API * (see include/linux/rpmsg.h). * * Returns 0 on success and an appropriate error value on failure. */ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, void *data, int len, bool wait) { struct virtproc_info *vrp = rpdev->vrp; struct device *dev = &rpdev->dev; struct scatterlist sg; struct rpmsg_hdr *msg; int err; /* bcasting isn't allowed */ if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) { dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst); return -EINVAL; } /* * We currently use fixed-sized buffers, and therefore the payload * length is limited. * * One of the possible improvements here is either to support * user-provided buffers (and then we can also support zero-copy * messaging), or to improve the buffer allocator, to support * variable-length buffer sizes. */ if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } /* grab a buffer */ msg = get_a_tx_buf(vrp); if (!msg && !wait) return -ENOMEM; /* no free buffer ? wait for one (but bail after 15 seconds) */ while (!msg) { /* enable "tx-complete" interrupts, if not already enabled */ rpmsg_upref_sleepers(vrp); /* * sleep until a free buffer is available or 15 secs elapse. * the timeout period is not configurable because there's * little point in asking drivers to specify that. * if later this happens to be required, it'd be easy to add. */ err = wait_event_interruptible_timeout(vrp->sendq, (msg = get_a_tx_buf(vrp)), msecs_to_jiffies(15000)); /* disable "tx-complete" interrupts if we're the last sleeper */ rpmsg_downref_sleepers(vrp); /* timeout ? */ if (!err) { dev_err(dev, "timeout waiting for a tx buffer\n"); return -ERESTARTSYS; } } msg->len = len; msg->flags = 0; msg->src = src; msg->dst = dst; msg->reserved = 0; memcpy(msg->data, data, len); dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n", msg->src, msg->dst, msg->len, msg->flags, msg->reserved); print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, msg, sizeof(*msg) + msg->len, true); sg_init_one(&sg, msg, sizeof(*msg) + len); mutex_lock(&vrp->tx_lock); /* add message to the remote processor's virtqueue */ err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL); if (err) { /* * need to reclaim the buffer here, otherwise it's lost * (memory won't leak, but rpmsg won't use it again for TX). * this will wait for a buffer management overhaul. */ dev_err(dev, "virtqueue_add_buf failed: %d\n", err); goto out; } /* tell the remote processor it has a pending message to read */ virtqueue_kick(vrp->svq); out: mutex_unlock(&vrp->tx_lock); return err; }
int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst, void *data, int len, bool wait) { struct virtproc_info *vrp = rpdev->vrp; struct device *dev = &rpdev->dev; struct scatterlist sg; struct rpmsg_hdr *msg; int err; if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) { dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst); return -EINVAL; } if (len > RPMSG_BUF_SIZE - sizeof(struct rpmsg_hdr)) { dev_err(dev, "message is too big (%d)\n", len); return -EMSGSIZE; } msg = get_a_tx_buf(vrp); if (!msg && !wait) return -ENOMEM; while (!msg) { rpmsg_upref_sleepers(vrp); err = wait_event_interruptible_timeout(vrp->sendq, (msg = get_a_tx_buf(vrp)), msecs_to_jiffies(15000)); rpmsg_downref_sleepers(vrp); if (!err) { dev_err(dev, "timeout waiting for a tx buffer\n"); return -ERESTARTSYS; } } msg->len = len; msg->flags = 0; msg->src = src; msg->dst = dst; msg->reserved = 0; memcpy(msg->data, data, len); dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n", msg->src, msg->dst, msg->len, msg->flags, msg->reserved); print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1, msg, sizeof(*msg) + msg->len, true); sg_init_one(&sg, msg, sizeof(*msg) + len); mutex_lock(&vrp->tx_lock); err = virtqueue_add_buf(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL); if (err < 0) { dev_err(dev, "virtqueue_add_buf failed: %d\n", err); goto out; } virtqueue_kick(vrp->svq); err = 0; out: mutex_unlock(&vrp->tx_lock); return err; }