mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-18 18:23:53 +08:00
USB gadget: Handle endpoint requests at the function level
Control requests targeted at an endpoint (that is sent to EP0 but specifying the target endpoint address in wIndex) are dispatched to the current configuration's setup callback, requiring all gadget drivers to dispatch the requests to the correct function driver. To avoid this, record which endpoints are used by each function in the composite driver SET CONFIGURATION handler and dispatch requests targeted at endpoints to the correct function. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Cc: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
4de8405759
commit
5242658d1b
@ -373,6 +373,8 @@ static void reset_config(struct usb_composite_dev *cdev)
|
|||||||
list_for_each_entry(f, &cdev->config->functions, list) {
|
list_for_each_entry(f, &cdev->config->functions, list) {
|
||||||
if (f->disable)
|
if (f->disable)
|
||||||
f->disable(f);
|
f->disable(f);
|
||||||
|
|
||||||
|
bitmap_zero(f->endpoints, 32);
|
||||||
}
|
}
|
||||||
cdev->config = NULL;
|
cdev->config = NULL;
|
||||||
}
|
}
|
||||||
@ -418,10 +420,35 @@ static int set_config(struct usb_composite_dev *cdev,
|
|||||||
/* Initialize all interfaces by setting them to altsetting zero. */
|
/* Initialize all interfaces by setting them to altsetting zero. */
|
||||||
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
|
for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
|
||||||
struct usb_function *f = c->interface[tmp];
|
struct usb_function *f = c->interface[tmp];
|
||||||
|
struct usb_descriptor_header **descriptors;
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Record which endpoints are used by the function. This is used
|
||||||
|
* to dispatch control requests targeted at that endpoint to the
|
||||||
|
* function's setup callback instead of the current
|
||||||
|
* configuration's setup callback.
|
||||||
|
*/
|
||||||
|
if (gadget->speed == USB_SPEED_HIGH)
|
||||||
|
descriptors = f->hs_descriptors;
|
||||||
|
else
|
||||||
|
descriptors = f->descriptors;
|
||||||
|
|
||||||
|
for (; *descriptors; ++descriptors) {
|
||||||
|
struct usb_endpoint_descriptor *ep;
|
||||||
|
int addr;
|
||||||
|
|
||||||
|
if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ep = (struct usb_endpoint_descriptor *)*descriptors;
|
||||||
|
addr = ((ep->bEndpointAddress & 0x80) >> 3)
|
||||||
|
| (ep->bEndpointAddress & 0x0f);
|
||||||
|
set_bit(addr, f->endpoints);
|
||||||
|
}
|
||||||
|
|
||||||
result = f->set_alt(f, tmp, 0);
|
result = f->set_alt(f, tmp, 0);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
|
DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
|
||||||
@ -688,6 +715,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
|
|||||||
u16 w_value = le16_to_cpu(ctrl->wValue);
|
u16 w_value = le16_to_cpu(ctrl->wValue);
|
||||||
u16 w_length = le16_to_cpu(ctrl->wLength);
|
u16 w_length = le16_to_cpu(ctrl->wLength);
|
||||||
struct usb_function *f = NULL;
|
struct usb_function *f = NULL;
|
||||||
|
u8 endp;
|
||||||
|
|
||||||
/* partial re-init of the response message; the function or the
|
/* partial re-init of the response message; the function or the
|
||||||
* gadget might need to intercept e.g. a control-OUT completion
|
* gadget might need to intercept e.g. a control-OUT completion
|
||||||
@ -800,23 +828,33 @@ unknown:
|
|||||||
ctrl->bRequestType, ctrl->bRequest,
|
ctrl->bRequestType, ctrl->bRequest,
|
||||||
w_value, w_index, w_length);
|
w_value, w_index, w_length);
|
||||||
|
|
||||||
/* functions always handle their interfaces ... punt other
|
/* functions always handle their interfaces and endpoints...
|
||||||
* recipients (endpoint, other, WUSB, ...) to the current
|
* punt other recipients (other, WUSB, ...) to the current
|
||||||
* configuration code.
|
* configuration code.
|
||||||
*
|
*
|
||||||
* REVISIT it could make sense to let the composite device
|
* REVISIT it could make sense to let the composite device
|
||||||
* take such requests too, if that's ever needed: to work
|
* take such requests too, if that's ever needed: to work
|
||||||
* in config 0, etc.
|
* in config 0, etc.
|
||||||
*/
|
*/
|
||||||
if ((ctrl->bRequestType & USB_RECIP_MASK)
|
switch (ctrl->bRequestType & USB_RECIP_MASK) {
|
||||||
== USB_RECIP_INTERFACE) {
|
case USB_RECIP_INTERFACE:
|
||||||
f = cdev->config->interface[intf];
|
f = cdev->config->interface[intf];
|
||||||
if (f && f->setup)
|
break;
|
||||||
value = f->setup(f, ctrl);
|
|
||||||
else
|
case USB_RECIP_ENDPOINT:
|
||||||
|
endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
|
||||||
|
list_for_each_entry(f, &cdev->config->functions, list) {
|
||||||
|
if (test_bit(endp, f->endpoints))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (&f->list == &cdev->config->functions)
|
||||||
f = NULL;
|
f = NULL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (value < 0 && !f) {
|
|
||||||
|
if (f && f->setup)
|
||||||
|
value = f->setup(f, ctrl);
|
||||||
|
else {
|
||||||
struct usb_configuration *c;
|
struct usb_configuration *c;
|
||||||
|
|
||||||
c = cdev->config;
|
c = cdev->config;
|
||||||
|
@ -127,6 +127,7 @@ struct usb_function {
|
|||||||
/* private: */
|
/* private: */
|
||||||
/* internals */
|
/* internals */
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
DECLARE_BITMAP(endpoints, 32);
|
||||||
};
|
};
|
||||||
|
|
||||||
int usb_add_function(struct usb_configuration *, struct usb_function *);
|
int usb_add_function(struct usb_configuration *, struct usb_function *);
|
||||||
|
Loading…
Reference in New Issue
Block a user