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

Commit e0857ce5 authored by Felipe Balbi's avatar Felipe Balbi
Browse files

usb: gadget: loopback: don't queue requests to bogus endpoints



A request allocated from e.g. ep1out cannot
be queued to any other endpoint. This bug has
been in f_loopback at least since mid-2011 and
since nobody has really screamed about it, we're
not backporting to any stable kernels.

Debugged with MUSB since that's the only controller
which complains about this case.

Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent c0d31b3c
Loading
Loading
Loading
Loading
+42 −45
Original line number Original line Diff line number Diff line
@@ -253,22 +253,13 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req)


	case 0:				/* normal completion? */
	case 0:				/* normal completion? */
		if (ep == loop->out_ep) {
		if (ep == loop->out_ep) {
			/* loop this OUT packet back IN to the host */
			req->zero = (req->actual < req->length);
			req->zero = (req->actual < req->length);
			req->length = req->actual;
			req->length = req->actual;
			status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
			if (status == 0)
				return;

			/* "should never get here" */
			ERROR(cdev, "can't loop %s to %s: %d\n",
				ep->name, loop->in_ep->name,
				status);
		}
		}


		/* queue the buffer for some later OUT packet */
		/* queue the buffer for some later OUT packet */
		req->length = buflen;
		req->length = buflen;
		status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
		status = usb_ep_queue(ep, req, GFP_ATOMIC);
		if (status == 0)
		if (status == 0)
			return;
			return;


@@ -308,60 +299,66 @@ static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
	return alloc_ep_req(ep, len, buflen);
	return alloc_ep_req(ep, len, buflen);
}
}


static int
static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop,
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
		struct usb_ep *ep)
{
{
	int					result = 0;
	struct usb_ep				*ep;
	struct usb_request			*req;
	struct usb_request			*req;
	unsigned				i;
	unsigned				i;
	int					result;


	/* one endpoint writes data back IN to the host */
	/*
	ep = loop->in_ep;
	 * one endpoint writes data back IN to the host while another endpoint
	 * just reads OUT packets
	 */
	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
	if (result)
	if (result)
		return result;
		goto fail0;
	result = usb_ep_enable(ep);
	result = usb_ep_enable(ep);
	if (result < 0)
	if (result < 0)
		return result;
	ep->driver_data = loop;

	/* one endpoint just reads OUT packets */
	ep = loop->out_ep;
	result = config_ep_by_speed(cdev->gadget, &(loop->function), ep);
	if (result)
		goto fail0;
		goto fail0;

	result = usb_ep_enable(ep);
	if (result < 0) {
fail0:
		ep = loop->in_ep;
		usb_ep_disable(ep);
		ep->driver_data = NULL;
		return result;
	}
	ep->driver_data = loop;
	ep->driver_data = loop;


	/* allocate a bunch of read buffers and queue them all at once.
	/*
	 * allocate a bunch of read buffers and queue them all at once.
	 * we buffer at most 'qlen' transfers; fewer if any need more
	 * we buffer at most 'qlen' transfers; fewer if any need more
	 * than 'buflen' bytes each.
	 * than 'buflen' bytes each.
	 */
	 */
	for (i = 0; i < qlen && result == 0; i++) {
	for (i = 0; i < qlen && result == 0; i++) {
		req = lb_alloc_ep_req(ep, 0);
		req = lb_alloc_ep_req(ep, 0);
		if (req) {
		if (!req)
			goto fail1;

		req->complete = loopback_complete;
		req->complete = loopback_complete;
		result = usb_ep_queue(ep, req, GFP_ATOMIC);
		result = usb_ep_queue(ep, req, GFP_ATOMIC);
			if (result)
		if (result) {
			ERROR(cdev, "%s queue req --> %d\n",
			ERROR(cdev, "%s queue req --> %d\n",
					ep->name, result);
					ep->name, result);
		} else {
			goto fail1;
			usb_ep_disable(ep);
		}
			ep->driver_data = NULL;
			result = -ENOMEM;
			goto fail0;
	}
	}

	return 0;

fail1:
	usb_ep_disable(ep);

fail0:
	return result;
}
}


static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
	int					result = 0;

	result = enable_endpoint(cdev, loop, loop->in_ep);
	if (result)
		return result;

	result = enable_endpoint(cdev, loop, loop->out_ep);
	if (result)
		return result;

	DBG(cdev, "%s enabled\n", loop->function.name);
	DBG(cdev, "%s enabled\n", loop->function.name);
	return result;
	return result;
}
}