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

Commit 8c6a54ca authored by Ligui Deng's avatar Ligui Deng
Browse files

usb: gadget: cdev: Add single packet and dynamic buffer support for Rx path



Display tearing functionality on SXR devices makes use of
both  bulk-in and bulk-out endpoints of cdev driver
to acquire real time audio/video data from usb host.

Currently if the userspace application making use of cdev
driver requests 50KB of data from host, the cdev_read function
queues multiple usb requests to host till it gets 50KB of data.
In Display Tearing, although the userspace application requests
50KB of data per frame, the actual amount sent by host for every
frame is variable and can be less than the requested amount. If
host is sending packets of size less that requested amount per frame,
driver ends up appending multiple frames in a single cdev_read call
and returns corrupt information to userspace causing low frame
rate on display.

To resolve this add a configfs property to specify the buffer size
for every usb request on out ep and a flag to specify if we need
single packet mode. If single packet mode is enabled, only one usb
request is queued on out endpoint per cdev_read call and buffer
corresponding to that request contains only one frame of data coming
from host. The driver appends data from one usb request in read_queued
pool to userspace buffer to avoid corrupting video frame information.

CRs-Fixed: 3038067
Change-Id: I1ba0b954d14f187eeddc511f4ac199784248b33b
Signed-off-by: default avatarLigui Deng <ldeng@codeaurora.org>
parent 01d1a8c4
Loading
Loading
Loading
Loading
+84 −3
Original line number Diff line number Diff line
@@ -128,6 +128,10 @@ struct f_cdev {
	unsigned long           nbytes_to_port_bridge;
	unsigned long		nbytes_from_port_bridge;

	unsigned int		single_packet_mode;
	unsigned int		rx_buf_size;
	unsigned int		rx_queue_size;

	struct dentry		*debugfs_root;

	/* To test remote wakeup using debugfs */
@@ -140,6 +144,7 @@ struct f_cdev_opts {
	char *func_name;
	u8 port_num;
	u8 proto;
	int refcnt;
};

static int major, minors;
@@ -406,7 +411,14 @@ static void port_complete_set_line_coding(struct usb_ep *ep,

static void usb_cser_free_func(struct usb_function *f)
{
	/* Do nothing as cser_alloc() doesn't alloc anything. */
	unsigned long flags;
	struct f_cdev_opts *opts = container_of(f->fi, struct f_cdev_opts,
			func_inst);
	struct f_cdev *port = opts->port;

	spin_lock_irqsave(&port->port_lock, flags);
	opts->refcnt--;
	spin_unlock_irqrestore(&port->port_lock, flags);
}

static int
@@ -950,7 +962,7 @@ static void usb_cser_start_rx(struct f_cdev *port)

		req = list_entry(pool->next, struct usb_request, list);
		list_del_init(&req->list);
		req->length = BRIDGE_RX_BUF_SIZE;
		req->length = port->rx_buf_size;
		req->complete = usb_cser_read_complete;
		spin_unlock_irqrestore(&port->port_lock, flags);
		ret = usb_ep_queue(ep, req, GFP_KERNEL);
@@ -1047,7 +1059,9 @@ static void usb_cser_start_io(struct f_cdev *port)

	ret = usb_cser_alloc_requests(port->port_usb.out,
				&port->read_pool,
				BRIDGE_RX_QUEUE_SIZE, BRIDGE_RX_BUF_SIZE, 0,
				port->rx_queue_size,
				port->rx_buf_size,
				0,
				usb_cser_read_complete);
	if (ret) {
		pr_err("unable to allocate out requests\n");
@@ -1242,6 +1256,8 @@ ssize_t f_cdev_read(struct file *file,
			current_rx_req = NULL;
			current_rx_buf = NULL;
		}
		if (port->single_packet_mode)
			break;
	}

	port->pending_rx_bytes = pending_rx_bytes;
@@ -1766,6 +1782,11 @@ static struct f_cdev *f_cdev_alloc(char *func_name, int portno)
	INIT_LIST_HEAD(&port->read_queued);
	INIT_LIST_HEAD(&port->write_pool);

	/* Fill rx bridge parameters */
	port->rx_buf_size = BRIDGE_RX_BUF_SIZE;
	port->rx_queue_size = BRIDGE_RX_QUEUE_SIZE;
	port->single_packet_mode = 0;

	port->fcdev_wq = create_singlethread_workqueue(port->name);
	if (!port->fcdev_wq) {
		pr_err("Unable to create workqueue fcdev_wq for port:%s\n",
@@ -1932,9 +1953,64 @@ static ssize_t usb_cser_status_store(struct config_item *item,
	return len;
}

#define CDEV_BUF_ATTRIBUTE(name)					\
static ssize_t usb_cser_##name##_show(struct config_item *item,		\
			char *page)					\
{									\
	struct f_cdev *port = to_f_cdev_opts(item)->port;		\
	unsigned long flags;						\
	int ret;							\
									\
	spin_lock_irqsave(&port->port_lock, flags);			\
	ret = scnprintf(page, PAGE_SIZE, "%u\n",			\
			port->name);					\
	spin_unlock_irqrestore(&port->port_lock, flags);		\
									\
	return ret;							\
}									\
									\
static ssize_t usb_cser_##name##_store(struct config_item *item,	\
			const char *page, size_t len)			\
{									\
	struct f_cdev_opts *opts = to_f_cdev_opts(item);		\
	struct f_cdev *port = opts->port;				\
	unsigned long flags;						\
	int ret;							\
	unsigned int val;						\
									\
	spin_lock_irqsave(&port->port_lock, flags);			\
	if (opts->refcnt) {						\
		ret = -EBUSY;						\
		goto end;						\
	}								\
									\
	ret = kstrtouint(page, 0, &val);				\
	if (ret)							\
		goto end;						\
									\
	port->name = val;						\
	ret = len;							\
									\
end:									\
	spin_unlock_irqrestore(&port->port_lock, flags);		\
	return ret;							\
}									\
									\

CDEV_BUF_ATTRIBUTE(rx_buf_size);
CDEV_BUF_ATTRIBUTE(rx_queue_size);
CDEV_BUF_ATTRIBUTE(single_packet_mode);

CONFIGFS_ATTR(usb_cser_, single_packet_mode);
CONFIGFS_ATTR(usb_cser_, status);
CONFIGFS_ATTR(usb_cser_, rx_buf_size);
CONFIGFS_ATTR(usb_cser_, rx_queue_size);

static struct configfs_attribute *cserial_attrs[] = {
	&usb_cser_attr_single_packet_mode,
	&usb_cser_attr_status,
	&usb_cser_attr_rx_buf_size,
	&usb_cser_attr_rx_queue_size,
	NULL,
};

@@ -2045,6 +2121,11 @@ static struct usb_function *cser_alloc(struct usb_function_instance *fi)
{
	struct f_cdev_opts *opts = to_fi_cdev_opts(fi);
	struct f_cdev *port = opts->port;
	unsigned long flags;

	spin_lock_irqsave(&port->port_lock, flags);
	opts->refcnt++;
	spin_unlock_irqrestore(&port->port_lock, flags);

	port->port_usb.func.name = "cser";
	port->port_usb.func.strings = usb_cser_strings;