summaryrefslogtreecommitdiff
path: root/drivers/rpmsg
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rpmsg')
-rw-r--r--drivers/rpmsg/qcom_glink_native.c75
1 files changed, 50 insertions, 25 deletions
diff --git a/drivers/rpmsg/qcom_glink_native.c b/drivers/rpmsg/qcom_glink_native.c
index aea8f5955e34..5dccbf18046e 100644
--- a/drivers/rpmsg/qcom_glink_native.c
+++ b/drivers/rpmsg/qcom_glink_native.c
@@ -162,7 +162,7 @@ struct glink_channel {
spinlock_t intent_lock;
struct idr liids;
- void *buf;
+ struct glink_core_rx_intent *buf;
int buf_offset;
int buf_size;
@@ -614,6 +614,7 @@ static int qcom_glink_rx_defer(struct qcom_glink *glink, size_t extra)
static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
{
+ struct glink_core_rx_intent *intent;
struct glink_channel *channel;
struct {
struct glink_msg msg;
@@ -623,6 +624,8 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
unsigned int chunk_size;
unsigned int left_size;
unsigned int rcid;
+ unsigned int liid;
+ int ret = 0;
unsigned long flags;
if (avail < sizeof(hdr)) {
@@ -650,56 +653,78 @@ static int qcom_glink_rx_data(struct qcom_glink *glink, size_t avail)
dev_dbg(glink->dev, "Data on non-existing channel\n");
/* Drop the message */
- qcom_glink_rx_advance(glink,
- ALIGN(sizeof(hdr) + chunk_size, 8));
- return 0;
+ goto advance_rx;
}
- /* Might have an ongoing, fragmented, message to append */
- if (!channel->buf) {
- channel->buf = kmalloc(chunk_size + left_size, GFP_ATOMIC);
- if (!channel->buf)
- return -ENOMEM;
+ if (glink->intentless) {
+ /* Might have an ongoing, fragmented, message to append */
+ if (!channel->buf) {
+ intent = kzalloc(sizeof(*intent), GFP_ATOMIC);
+ if (!intent)
+ return -ENOMEM;
+
+ intent->data = kmalloc(chunk_size + left_size,
+ GFP_ATOMIC);
+ if (!intent->data) {
+ kfree(intent);
+ return -ENOMEM;
+ }
+
+ intent->id = 0xbabababa;
+ intent->size = chunk_size + left_size;
+ intent->offset = 0;
+
+ channel->buf = intent;
+ } else {
+ intent = channel->buf;
+ }
+ } else {
+ liid = le32_to_cpu(hdr.msg.param2);
- channel->buf_size = chunk_size + left_size;
- channel->buf_offset = 0;
- }
+ spin_lock_irqsave(&channel->intent_lock, flags);
+ intent = idr_find(&channel->liids, liid);
+ spin_unlock_irqrestore(&channel->intent_lock, flags);
- qcom_glink_rx_advance(glink, sizeof(hdr));
+ if (!intent) {
+ dev_err(glink->dev,
+ "no intent found for channel %s intent %d",
+ channel->name, liid);
+ goto advance_rx;
+ }
+ }
- if (channel->buf_size - channel->buf_offset < chunk_size) {
- dev_err(glink->dev, "Insufficient space in input buffer\n");
+ if (intent->size - intent->offset < chunk_size) {
+ dev_err(glink->dev, "Insufficient space in intent\n");
/* The packet header lied, drop payload */
- qcom_glink_rx_advance(glink, chunk_size);
- return -ENOMEM;
+ goto advance_rx;
}
- qcom_glink_rx_peak(glink, channel->buf + channel->buf_offset,
+ qcom_glink_rx_advance(glink, ALIGN(sizeof(hdr), 8));
+ qcom_glink_rx_peak(glink, intent->data + intent->offset,
chunk_size);
- channel->buf_offset += chunk_size;
+ intent->offset += chunk_size;
/* Handle message when no fragments remain to be received */
if (!left_size) {
spin_lock(&channel->recv_lock);
if (channel->ept.cb) {
channel->ept.cb(channel->ept.rpdev,
- channel->buf,
- channel->buf_offset,
+ intent->data,
+ intent->offset,
channel->ept.priv,
RPMSG_ADDR_ANY);
}
spin_unlock(&channel->recv_lock);
- kfree(channel->buf);
+ intent->offset = 0;
channel->buf = NULL;
- channel->buf_size = 0;
}
- /* Each message starts at 8 byte aligned address */
+advance_rx:
qcom_glink_rx_advance(glink, ALIGN(chunk_size, 8));
- return 0;
+ return ret;
}
static int qcom_glink_rx_open_ack(struct qcom_glink *glink, unsigned int lcid)