diff options
Diffstat (limited to 'drivers/usb/gadget/composite.c')
| -rw-r--r-- | drivers/usb/gadget/composite.c | 121 |
1 files changed, 57 insertions, 64 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index dd9b90481b4c..5b3866909b75 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -19,7 +19,7 @@ #include <linux/usb/composite.h> #include <linux/usb/otg.h> #include <linux/usb/webusb.h> -#include <asm/unaligned.h> +#include <linux/unaligned.h> #include "u_os_desc.h" @@ -170,33 +170,27 @@ int config_ep_by_speed_and_alt(struct usb_gadget *g, /* select desired speed */ switch (g->speed) { case USB_SPEED_SUPER_PLUS: - if (gadget_is_superspeed_plus(g)) { - if (f->ssp_descriptors) { - speed_desc = f->ssp_descriptors; - want_comp_desc = 1; - break; - } - incomplete_desc = true; + if (f->ssp_descriptors) { + speed_desc = f->ssp_descriptors; + want_comp_desc = 1; + break; } + incomplete_desc = true; fallthrough; case USB_SPEED_SUPER: - if (gadget_is_superspeed(g)) { - if (f->ss_descriptors) { - speed_desc = f->ss_descriptors; - want_comp_desc = 1; - break; - } - incomplete_desc = true; + if (f->ss_descriptors) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; } + incomplete_desc = true; fallthrough; case USB_SPEED_HIGH: - if (gadget_is_dualspeed(g)) { - if (f->hs_descriptors) { - speed_desc = f->hs_descriptors; - break; - } - incomplete_desc = true; + if (f->hs_descriptors) { + speed_desc = f->hs_descriptors; + break; } + incomplete_desc = true; fallthrough; default: speed_desc = f->fs_descriptors; @@ -1017,7 +1011,7 @@ static int set_config(struct usb_composite_dev *cdev, ep = (struct usb_endpoint_descriptor *)*descriptors; addr = ((ep->bEndpointAddress & 0x80) >> 3) - | (ep->bEndpointAddress & 0x0f); + | usb_endpoint_num(ep); set_bit(addr, f->endpoints); } @@ -1056,10 +1050,11 @@ static int set_config(struct usb_composite_dev *cdev, else usb_gadget_set_remote_wakeup(gadget, 0); done: - if (power <= USB_SELF_POWER_VBUS_MAX_DRAW) - usb_gadget_set_selfpowered(gadget); - else + if (power > USB_SELF_POWER_VBUS_MAX_DRAW || + (c && !(c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, power); if (result >= 0 && cdev->delayed_status) @@ -1199,30 +1194,6 @@ static void remove_config(struct usb_composite_dev *cdev, } } -/** - * usb_remove_config() - remove a configuration from a device. - * @cdev: wraps the USB gadget - * @config: the configuration - * - * Drivers must call usb_gadget_disconnect before calling this function - * to disconnect the device from the host and make sure the host will not - * try to enumerate the device while we are changing the config list. - */ -void usb_remove_config(struct usb_composite_dev *cdev, - struct usb_configuration *config) -{ - unsigned long flags; - - spin_lock_irqsave(&cdev->lock, flags); - - if (cdev->config == config) - reset_config(cdev); - - spin_unlock_irqrestore(&cdev->lock, flags); - - remove_config(cdev, config); -} - /*-------------------------------------------------------------------------*/ /* We support strings in multiple languages ... string descriptor zero @@ -1850,7 +1821,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0200); } - value = min(w_length, (u16) sizeof cdev->desc); + value = min_t(u16, w_length, sizeof(cdev->desc)); memcpy(req->buf, &cdev->desc, value); break; case USB_DT_DEVICE_QUALIFIER: @@ -1869,19 +1840,19 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_STRING: value = get_string(cdev, req->buf, w_index, w_value & 0xff); if (value >= 0) - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); break; case USB_DT_BOS: if (gadget_is_superspeed(gadget) || gadget->lpm_capable || cdev->use_webusb) { value = bos_desc(cdev); - value = min(w_length, (u16) value); + value = min_t(u16, w_length, value); } break; case USB_DT_OTG: @@ -1936,7 +1907,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) *(u8 *)req->buf = cdev->config->bConfigurationValue; else *(u8 *)req->buf = 0; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; /* function drivers must handle get/set altsetting */ @@ -1982,7 +1953,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (value < 0) break; *((u8 *)req->buf) = value; - value = min(w_length, (u16) 1); + value = min_t(u16, w_length, 1); break; case USB_REQ_GET_STATUS: if (gadget_is_otg(gadget) && gadget->hnp_polling_support && @@ -2016,15 +1987,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (f->get_status) { status = f->get_status(f); + if (status < 0) break; - } else { - /* Set D0 and D1 bits based on func wakeup capability */ - if (f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP) { - status |= USB_INTRF_STAT_FUNC_RW_CAP; - if (f->func_wakeup_armed) - status |= USB_INTRF_STAT_FUNC_RW; - } + + /* if D5 is not set, then device is not wakeup capable */ + if (!(f->config->bmAttributes & USB_CONFIG_ATT_WAKEUP)) + status &= ~(USB_INTRF_STAT_FUNC_RW_CAP | USB_INTRF_STAT_FUNC_RW); } put_unaligned_le16(status & 0x0000ffff, req->buf); @@ -2117,6 +2086,18 @@ unknown: memset(buf, 0, w_length); buf[5] = 0x01; switch (ctrl->bRequestType & USB_RECIP_MASK) { + /* + * The Microsoft CompatID OS Descriptor Spec(w_index = 0x4) and + * Extended Prop OS Desc Spec(w_index = 0x5) state that the + * HighByte of wValue is the InterfaceNumber and the LowByte is + * the PageNumber. This high/low byte ordering is incorrectly + * documented in the Spec. USB analyzer output on the below + * request packets show the high/low byte inverted i.e LowByte + * is the InterfaceNumber and the HighByte is the PageNumber. + * Since we dont support >64KB CompatID/ExtendedProp descriptors, + * PageNumber is set to 0. Hence verify that the HighByte is 0 + * for below two cases. + */ case USB_RECIP_DEVICE: if (w_index != 0x4 || (w_value >> 8)) break; @@ -2484,6 +2465,11 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev, if (!cdev->os_desc_req->buf) { ret = -ENOMEM; usb_ep_free_request(ep0, cdev->os_desc_req); + /* + * Set os_desc_req to NULL so that composite_dev_cleanup() + * will not try to free it again. + */ + cdev->os_desc_req = NULL; goto end; } cdev->os_desc_req->context = cdev; @@ -2609,7 +2595,10 @@ void composite_suspend(struct usb_gadget *gadget) cdev->suspended = 1; - usb_gadget_set_selfpowered(gadget); + if (cdev->config && + cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER) + usb_gadget_set_selfpowered(gadget); + usb_gadget_vbus_draw(gadget, 2); } @@ -2643,8 +2632,11 @@ void composite_resume(struct usb_gadget *gadget) else maxpower = min(maxpower, 900U); - if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW) + if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW || + !(cdev->config->bmAttributes & USB_CONFIG_ATT_SELFPOWER)) usb_gadget_clear_selfpowered(gadget); + else + usb_gadget_set_selfpowered(gadget); usb_gadget_vbus_draw(gadget, maxpower); } else { @@ -2805,5 +2797,6 @@ void usb_composite_overwrite_options(struct usb_composite_dev *cdev, } EXPORT_SYMBOL_GPL(usb_composite_overwrite_options); +MODULE_DESCRIPTION("infrastructure for Composite USB Gadgets"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); |
