Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85f2a837 authored by Sarannya S's avatar Sarannya S Committed by Deepak Kumar Singh
Browse files

drivers: char: add stream like read support



Some user space clients with stream like reading may read
partial data from skb. Currently only reading of entire skb
is supported.

Adding support to read smaller chunks from an entire skb.

Change-Id: I7789bea78debab8ece517a99a0066eccbf9a64ec
Signed-off-by: default avatarSarannya S <sarannya@codeaurora.org>
Signed-off-by: default avatarDeepak Kumar Singh <deesin@codeaurora.org>
Signed-off-by: default avatarshouyi Hu <shouhu@codeaurora.org>
parent ef94cac2
Loading
Loading
Loading
Loading
+78 −18
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020, The Linux Foundation. All rights reserved.
/* Copyright (c) 2021, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -46,10 +46,14 @@
 * @queue:      incoming message queue
 * @readq:      wait object for incoming queue
 * @sig_change: flag to indicate serial signal change
 * @fragmented_read: set from dt node for partial read
 * @dev_name:   /dev/@dev_name for smd_pkt device
 * @ch_name:    smd channel to match to
 * @edge:       smd edge to match to
 * @open_tout:  timeout for open syscall, configurable in sysfs
 * @rskb:       current skb being read
 * @rdata:      data pointer in current skb
 * @rdata_len:  remaining data to be read from skb
 */
struct smd_pkt_dev {

@@ -65,10 +69,14 @@ struct smd_pkt_dev {
	struct sk_buff_head queue;
	wait_queue_head_t readq;
	int sig_change;
	bool fragmented_read;
	const char *dev_name;
	const char *ch_name;
	const char *edge;
	int open_tout;
	struct sk_buff *rskb;
	unsigned char *rdata;
	size_t rdata_len;
};

#define dev_to_smd_pkt_devp(_dev) container_of(_dev, struct smd_pkt_dev, dev)
@@ -333,7 +341,6 @@ ssize_t smd_pkt_read(struct file *file,

	struct smd_pkt_dev *smd_pkt_devp = file->private_data;
	unsigned long flags;
	struct sk_buff *skb;
	int use;

	if (!smd_pkt_devp ||
@@ -347,15 +354,17 @@ ssize_t smd_pkt_read(struct file *file,
		return -ENETRESET;
	}

			SMD_PKT_INFO("begin for %s by %s:%d ref_cnt[%d]\n",
	SMD_PKT_INFO(
		"begin for %s by %s:%d ref_cnt[%d], remaining[%d], count[%d]\n",
			smd_pkt_devp->ch_name, current->comm,
			task_pid_nr(current),
			refcount_read(&smd_pkt_devp->refcount));
			refcount_read(&smd_pkt_devp->refcount),
			smd_pkt_devp->rdata_len, count);

	spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);

	/* Wait for data in the queue */
	if (skb_queue_empty(&smd_pkt_devp->queue)) {
	if (skb_queue_empty(&smd_pkt_devp->queue) && !smd_pkt_devp->rskb) {
		spin_unlock_irqrestore(&smd_pkt_devp->queue_lock, flags);

		if (file->f_flags & O_NONBLOCK)
@@ -370,19 +379,53 @@ ssize_t smd_pkt_read(struct file *file,
		spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);
	}

	skb = skb_dequeue(&smd_pkt_devp->queue);
	spin_unlock_irqrestore(&smd_pkt_devp->queue_lock, flags);
	if (!skb)
	if (!smd_pkt_devp->rskb) {
		smd_pkt_devp->rskb = skb_dequeue(&smd_pkt_devp->queue);
		if (!smd_pkt_devp->rskb) {
			spin_unlock_irqrestore(&smd_pkt_devp->queue_lock,
					       flags);
			return -EFAULT;
		}
		smd_pkt_devp->rdata = smd_pkt_devp->rskb->data;
		smd_pkt_devp->rdata_len = smd_pkt_devp->rskb->len;
	}
	spin_unlock_irqrestore(&smd_pkt_devp->queue_lock, flags);

	use = min_t(size_t, count, skb->len);
	if (copy_to_user(buf, skb->data, use))
	use = min_t(size_t, count, smd_pkt_devp->rdata_len);

	if (copy_to_user(buf, smd_pkt_devp->rdata, use))
		use = -EFAULT;

	if (!smd_pkt_devp->fragmented_read && smd_pkt_devp->rdata_len == use) {
		struct sk_buff *skb = smd_pkt_devp->rskb;

		spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);
		smd_pkt_devp->rskb = NULL;
		smd_pkt_devp->rdata = NULL;
		smd_pkt_devp->rdata_len = 0;
		spin_unlock_irqrestore(&smd_pkt_devp->queue_lock, flags);

		kfree_skb(skb);
	} else {
		struct sk_buff *skb = NULL;

	SMD_PKT_INFO("end for %s by %s:%d ret[%d]\n", smd_pkt_devp->ch_name,
			current->comm, task_pid_nr(current), use);
		spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);
		smd_pkt_devp->rdata += use;
		smd_pkt_devp->rdata_len -= use;
		if (smd_pkt_devp->rdata_len == 0) {
			skb = smd_pkt_devp->rskb;
			smd_pkt_devp->rskb = NULL;
			smd_pkt_devp->rdata = NULL;
			smd_pkt_devp->rdata_len = 0;
		}
		spin_unlock_irqrestore(&smd_pkt_devp->queue_lock, flags);
		if (skb)
			kfree_skb(skb);
	}

	SMD_PKT_INFO("end for %s by %s:%d ret[%d], remaining[%d]\n",
			smd_pkt_devp->ch_name, current->comm,
			task_pid_nr(current), use, smd_pkt_devp->rdata_len);

	return use;
}
@@ -487,7 +530,7 @@ static unsigned int smd_pkt_poll(struct file *file, poll_table *wait)
	}

	spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);
	if (!skb_queue_empty(&smd_pkt_devp->queue))
	if (!skb_queue_empty(&smd_pkt_devp->queue) || smd_pkt_devp->rskb)
		mask |= POLLIN | POLLRDNORM;

	if (smd_pkt_devp->sig_change)
@@ -586,6 +629,13 @@ int smd_pkt_release(struct inode *inode, struct file *file)
		spin_lock_irqsave(&smd_pkt_devp->queue_lock, flags);

		/* Discard all SKBs */
		if (smd_pkt_devp->rskb) {
			kfree_skb(smd_pkt_devp->rskb);
			smd_pkt_devp->rskb = NULL;
			smd_pkt_devp->rdata = NULL;
			smd_pkt_devp->rdata_len = 0;
		}

		while (!skb_queue_empty(&smd_pkt_devp->queue)) {
			skb = skb_dequeue(&smd_pkt_devp->queue);
			kfree_skb(skb);
@@ -658,9 +708,14 @@ static int smd_pkt_parse_devicetree(struct device_node *np,
	if (ret < 0)
		goto error;

	key = "qcom,smdpkt-fragmented-read";

	smd_pkt_devp->fragmented_read = of_property_read_bool(np, key);

	SMD_PKT_INFO("Parsed %s:%s /dev/%s\n", smd_pkt_devp->edge,
						smd_pkt_devp->ch_name,
						smd_pkt_devp->dev_name);
						smd_pkt_devp->dev_name,
						smd_pkt_devp->fragmented_read);
	return 0;

error:
@@ -741,6 +796,11 @@ static int smd_pkt_create_device(struct device *parent,
	smd_pkt_devp->sig_change = false;

	spin_lock_init(&smd_pkt_devp->queue_lock);

	smd_pkt_devp->rskb = NULL;
	smd_pkt_devp->rdata = NULL;
	smd_pkt_devp->rdata_len = 0;

	skb_queue_head_init(&smd_pkt_devp->queue);
	init_waitqueue_head(&smd_pkt_devp->readq);