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

Commit a238e35b authored by Ajay Agarwal's avatar Ajay Agarwal
Browse files

usb: gadget: u_smd: Allocate requests in gsmd_connect



Currently we are allocating and freeing requests from start_io
and stop_io respectively. If SMD notifies channel close with
the read requests still queued with the hardware, then with DWC3
gadget the requests are not returned to the read pool and we try
to free the requests from stop_io. But the requests are not
freed leading to extra memory allocation on next start_io.

Fix this by allocating and freeing requests from cable connect
status change i.e. gsmd_connect and gsmd_disconnect respectively.
Also return the requests to read and write pools on unsuccessful
completion, or if requests are pending in read_queue.

Change-Id: I119d03ab14ac7a4f0cea677f0804229f95468d47
Signed-off-by: default avatarAjay Agarwal <ajaya@codeaurora.org>
parent 4a3f15e3
Loading
Loading
Loading
Loading
+54 −43
Original line number Diff line number Diff line
/*
 * u_smd.c - utilities for USB gadget serial over smd
 *
 * Copyright (c) 2011, 2013-2017, The Linux Foundation. All rights reserved.
 * Copyright (c) 2011, 2013-2018, The Linux Foundation. All rights reserved.
 *
 * This code also borrows from drivers/usb/gadget/u_serial.c, which is
 * Copyright (C) 2000 - 2003 Al Borchers (alborchers@steinerpoint.com)
@@ -402,8 +402,8 @@ static void gsmd_read_complete(struct usb_ep *ep, struct usb_request *req)
	spin_lock(&port->port_lock);
	if (!test_bit(CH_OPENED, &port->pi->flags) ||
			req->status == -ESHUTDOWN) {
		list_add_tail(&req->list, &port->read_pool);
		spin_unlock(&port->port_lock);
		gsmd_free_req(ep, req);
		return;
	}

@@ -428,8 +428,8 @@ static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)
	spin_lock(&port->port_lock);
	if (!test_bit(CH_OPENED, &port->pi->flags) ||
			req->status == -ESHUTDOWN) {
		list_add(&req->list, &port->write_pool);
		spin_unlock(&port->port_lock);
		gsmd_free_req(ep, req);
		return;
	}

@@ -447,46 +447,21 @@ static void gsmd_write_complete(struct usb_ep *ep, struct usb_request *req)

static void gsmd_start_io(struct gsmd_port *port)
{
	int		ret = -ENODEV;

	pr_debug("%s: port: %pK\n", __func__, port);

	spin_lock(&port->port_lock);

	if (!port->port_usb)
		goto start_io_out;
	if (!port->port_usb) {
		spin_unlock(&port->port_lock);
		return;
	}

	smd_tiocmset_from_cb(port->pi->ch,
			port->cbits_to_modem,
			~port->cbits_to_modem);

	ret = gsmd_alloc_requests(port->port_usb->out,
				&port->read_pool,
				SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE, 0,
				gsmd_read_complete);
	if (ret) {
		pr_err("%s: unable to allocate out requests\n",
				__func__);
		goto start_io_out;
	}

	ret = gsmd_alloc_requests(port->port_usb->in,
				&port->write_pool,
				SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE, extra_sz,
				gsmd_write_complete);
	if (ret) {
		gsmd_free_requests(port->port_usb->out, &port->read_pool);
		pr_err("%s: unable to allocate IN requests\n",
				__func__);
		goto start_io_out;
	}

start_io_out:
	spin_unlock(&port->port_lock);

	if (ret)
		return;

	gsmd_start_rx(port);
}

@@ -528,6 +503,7 @@ static void gsmd_stop_io(struct gsmd_port *port)
	struct usb_ep	*in;
	struct usb_ep	*out;
	unsigned long	flags;
	struct list_head *q;

	spin_lock_irqsave(&port->port_lock, flags);
	if (!port->port_usb) {
@@ -542,17 +518,22 @@ static void gsmd_stop_io(struct gsmd_port *port)
	usb_ep_fifo_flush(out);

	spin_lock(&port->port_lock);
	if (port->port_usb) {
		gsmd_free_requests(out, &port->read_pool);
		gsmd_free_requests(out, &port->read_queue);
		gsmd_free_requests(in, &port->write_pool);
		port->n_read = 0;
		port->cbits_to_laptop = 0;
	} else {
	if (!port->port_usb) {
		spin_unlock(&port->port_lock);
		return;
	}

	q = &port->read_queue;
	while (!list_empty(q)) {
		struct usb_request *req;

		req = list_first_entry(q, struct usb_request, list);
		list_move(&req->list, &port->read_pool);
	}

	port->n_read = 0;
	port->cbits_to_laptop = 0;

	if (port->port_usb->send_modem_ctrl_bits)
		port->port_usb->send_modem_ctrl_bits(
					port->port_usb,
@@ -721,14 +702,36 @@ int gsmd_connect(struct gserial *gser, u8 portno)
	port->nbytes_tomodem = 0;
	port->nbytes_tolaptop = 0;
	port->is_suspended = false;
	ret = gsmd_alloc_requests(port->port_usb->out,
				&port->read_pool,
				SMD_RX_QUEUE_SIZE, SMD_RX_BUF_SIZE, 0,
				gsmd_read_complete);
	if (ret) {
		pr_err("%s: unable to allocate out requests\n",
				__func__);
		spin_unlock_irqrestore(&port->port_lock, flags);
		return ret;
	}

	ret = gsmd_alloc_requests(port->port_usb->in,
				&port->write_pool,
				SMD_TX_QUEUE_SIZE, SMD_TX_BUF_SIZE, extra_sz,
				gsmd_write_complete);
	if (ret) {
		gsmd_free_requests(port->port_usb->out, &port->read_pool);
		pr_err("%s: unable to allocate IN requests\n",
				__func__);
		spin_unlock_irqrestore(&port->port_lock, flags);
		return ret;
	}

	spin_unlock_irqrestore(&port->port_lock, flags);

	ret = usb_ep_enable(gser->in);
	if (ret) {
		pr_err("%s: usb_ep_enable failed eptype:IN ep:%pK, err:%d",
				__func__, gser->in, ret);
		port->port_usb = 0;
		return ret;
		goto free_req;
	}
	gser->in->driver_data = port;

@@ -736,15 +739,23 @@ int gsmd_connect(struct gserial *gser, u8 portno)
	if (ret) {
		pr_err("%s: usb_ep_enable failed eptype:OUT ep:%pK, err: %d",
				__func__, gser->out, ret);
		port->port_usb = 0;
		gser->in->driver_data = 0;
		return ret;
		goto free_req;
	}
	gser->out->driver_data = port;

	queue_delayed_work(gsmd_wq, &port->connect_work, msecs_to_jiffies(0));

	return 0;

free_req:
	spin_lock_irqsave(&port->port_lock, flags);
	gsmd_free_requests(port->port_usb->out, &port->write_pool);
	gsmd_free_requests(port->port_usb->out, &port->read_pool);
	port->port_usb = 0;
	spin_unlock_irqrestore(&port->port_lock, flags);

	return ret;
}

void gsmd_disconnect(struct gserial *gser, u8 portno)