From 04dd17822334871b23ea2862f7798fb0e0007777 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Sat, 11 May 2024 08:53:19 +0000 Subject: [PATCH] change otg to host mode --- kernel/drivers/usb/gadget/composite.c | 327 ++++++++++++++++++++++++------------------------------ 1 files changed, 145 insertions(+), 182 deletions(-) diff --git a/kernel/drivers/usb/gadget/composite.c b/kernel/drivers/usb/gadget/composite.c index deb8cfa..83df30b 100644 --- a/kernel/drivers/usb/gadget/composite.c +++ b/kernel/drivers/usb/gadget/composite.c @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/device.h> #include <linux/utsname.h> +#include <linux/bitfield.h> #include <linux/usb/composite.h> #include <linux/usb/otg.h> @@ -72,17 +73,17 @@ descriptors = f->ssp_descriptors; if (descriptors) break; - /* FALLTHROUGH */ + fallthrough; case USB_SPEED_SUPER: descriptors = f->ss_descriptors; if (descriptors) break; - /* FALLTHROUGH */ + fallthrough; case USB_SPEED_HIGH: descriptors = f->hs_descriptors; if (descriptors) break; - /* FALLTHROUGH */ + fallthrough; default: descriptors = f->fs_descriptors; } @@ -158,6 +159,8 @@ int want_comp_desc = 0; struct usb_descriptor_header **d_spd; /* cursor for speed desc */ + struct usb_composite_dev *cdev; + bool incomplete_desc = false; if (!g || !f || !_ep) return -EIO; @@ -166,27 +169,42 @@ switch (g->speed) { case USB_SPEED_SUPER_PLUS: if (gadget_is_superspeed_plus(g)) { - speed_desc = f->ssp_descriptors; - want_comp_desc = 1; - break; + if (f->ssp_descriptors) { + speed_desc = f->ssp_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; } - /* fall through */ + fallthrough; case USB_SPEED_SUPER: if (gadget_is_superspeed(g)) { - speed_desc = f->ss_descriptors; - want_comp_desc = 1; - break; + if (f->ss_descriptors) { + speed_desc = f->ss_descriptors; + want_comp_desc = 1; + break; + } + incomplete_desc = true; } - /* fall through */ + fallthrough; case USB_SPEED_HIGH: if (gadget_is_dualspeed(g)) { - speed_desc = f->hs_descriptors; - break; + if (f->hs_descriptors) { + speed_desc = f->hs_descriptors; + break; + } + incomplete_desc = true; } - /* fall through */ + fallthrough; default: speed_desc = f->fs_descriptors; } + + cdev = get_gadget_data(g); + if (incomplete_desc) + WARNING(cdev, + "%s doesn't hold the descriptors for current speed\n", + f->name); /* find correct alternate setting descriptor */ for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) { @@ -237,18 +255,14 @@ case USB_ENDPOINT_XFER_ISOC: /* mult: bits 1:0 of bmAttributes */ _ep->mult = (comp_desc->bmAttributes & 0x3) + 1; - /* fall through */ + fallthrough; case USB_ENDPOINT_XFER_BULK: case USB_ENDPOINT_XFER_INT: _ep->maxburst = comp_desc->bMaxBurst + 1; break; default: - if (comp_desc->bMaxBurst != 0) { - struct usb_composite_dev *cdev; - - cdev = get_gadget_data(g); + if (comp_desc->bMaxBurst != 0) ERROR(cdev, "ep0 bMaxBurst must be 0\n"); - } _ep->maxburst = 1; break; } @@ -476,122 +490,6 @@ } EXPORT_SYMBOL_GPL(usb_interface_id); -static int usb_func_wakeup_int(struct usb_function *func) -{ - int ret; - struct usb_gadget *gadget; - - if (!func || !func->config || !func->config->cdev || - !func->config->cdev->gadget) - return -EINVAL; - - pr_debug("%s - %s function wakeup\n", - __func__, func->name ? func->name : ""); - - gadget = func->config->cdev->gadget; - if ((gadget->speed != USB_SPEED_SUPER) || !func->func_wakeup_allowed) { - DBG(func->config->cdev, - "Function Wakeup is not possible. speed=%u, func_wakeup_allowed=%u\n", - gadget->speed, - func->func_wakeup_allowed); - - return -ENOTSUPP; - } - - ret = usb_gadget_func_wakeup(gadget, func->intf_id); - - return ret; -} - -/** - * usb_func_wakeup - wakes up a composite device function. - * @func: composite device function to wake up. - * - * Returns 0 on success or a negative error value. - */ -int usb_func_wakeup(struct usb_function *func) -{ - int ret; - unsigned long flags; - - if (!func || !func->config || !func->config->cdev) - return -EINVAL; - - pr_debug("%s function wakeup\n", - func->name ? func->name : ""); - - spin_lock_irqsave(&func->config->cdev->lock, flags); - ret = usb_func_wakeup_int(func); - if (ret == -EAGAIN) { - DBG(func->config->cdev, - "Function wakeup for %s could not complete due to suspend state. Delayed until after bus resume.\n", - func->name ? func->name : ""); - ret = 0; - } else if (ret < 0 && ret != -ENOTSUPP) { - ERROR(func->config->cdev, - "Failed to wake function %s from suspend state. ret=%d. Canceling USB request.\n", - func->name ? func->name : "", ret); - } - - spin_unlock_irqrestore(&func->config->cdev->lock, flags); - return ret; -} -EXPORT_SYMBOL_GPL(usb_func_wakeup); - -/** - * usb_func_ep_queue - queues (submits) an I/O request to a function endpoint. - * This function is similar to the usb_ep_queue function, but in addition it - * also checks whether the function is in Super Speed USB Function Suspend - * state, and if so a Function Wake notification is sent to the host - * (USB 3.0 spec, section 9.2.5.2). - * @func: the function which issues the USB I/O request. - * @ep:the endpoint associated with the request - * @req:the request being submitted - * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't - * pre-allocate all necessary memory with the request. - */ -int usb_func_ep_queue(struct usb_function *func, struct usb_ep *ep, - struct usb_request *req, gfp_t gfp_flags) -{ - int ret; - struct usb_gadget *gadget; - - if (!func || !func->config || !func->config->cdev || - !func->config->cdev->gadget || !ep || !req) { - ret = -EINVAL; - goto done; - } - - pr_debug("Function %s queueing new data into ep %u\n", - func->name ? func->name : "", ep->address); - - gadget = func->config->cdev->gadget; - if (func->func_is_suspended && func->func_wakeup_allowed) { - ret = usb_gadget_func_wakeup(gadget, func->intf_id); - if (ret == -EAGAIN) { - pr_debug("bus suspended func wakeup for %s delayed until bus resume.\n", - func->name ? func->name : ""); - } else if (ret < 0 && ret != -ENOTSUPP) { - pr_err("Failed to wake function %s from suspend state. ret=%d.\n", - func->name ? func->name : "", ret); - } - goto done; - } - - if (!func->func_is_suspended) - ret = 0; - - if (func->func_is_suspended && !func->func_wakeup_allowed) { - ret = -ENOTSUPP; - goto done; - } - - ret = usb_ep_queue(ep, req, gfp_flags); -done: - return ret; -} -EXPORT_SYMBOL_GPL(usb_func_ep_queue); - static u8 encode_bMaxPower(enum usb_device_speed speed, struct usb_configuration *c) { @@ -780,12 +678,36 @@ struct usb_ext_cap_descriptor *usb_ext; struct usb_dcd_config_params dcd_config_params; struct usb_bos_descriptor *bos = cdev->req->buf; + unsigned int besl = 0; bos->bLength = USB_DT_BOS_SIZE; bos->bDescriptorType = USB_DT_BOS; bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE); bos->bNumDeviceCaps = 0; + + /* Get Controller configuration */ + if (cdev->gadget->ops->get_config_params) { + cdev->gadget->ops->get_config_params(cdev->gadget, + &dcd_config_params); + } else { + dcd_config_params.besl_baseline = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.besl_deep = + USB_DEFAULT_BESL_UNSPECIFIED; + dcd_config_params.bU1devExitLat = + USB_DEFAULT_U1_DEV_EXIT_LAT; + dcd_config_params.bU2DevExitLat = + cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); + } + + if (dcd_config_params.besl_baseline != USB_DEFAULT_BESL_UNSPECIFIED) + besl = USB_BESL_BASELINE_VALID | + USB_SET_BESL_BASELINE(dcd_config_params.besl_baseline); + + if (dcd_config_params.besl_deep != USB_DEFAULT_BESL_UNSPECIFIED) + besl |= USB_BESL_DEEP_VALID | + USB_SET_BESL_DEEP(dcd_config_params.besl_deep); /* * A SuperSpeed device shall include the USB2.0 extension descriptor @@ -797,7 +719,8 @@ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE; usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY; usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT; - usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | USB_BESL_SUPPORT); + usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT | + USB_BESL_SUPPORT | besl); /* * The Superspeed USB Capability descriptor shall be implemented by all @@ -818,17 +741,6 @@ USB_HIGH_SPEED_OPERATION | USB_5GBPS_OPERATION); ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; - - /* Get Controller configuration */ - if (cdev->gadget->ops->get_config_params) { - cdev->gadget->ops->get_config_params( - &dcd_config_params); - } else { - dcd_config_params.bU1devExitLat = - USB_DEFAULT_U1_DEV_EXIT_LAT; - dcd_config_params.bU2DevExitLat = - cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT); - } ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat; ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat; } @@ -836,47 +748,77 @@ /* The SuperSpeedPlus USB Device Capability descriptor */ if (gadget_is_superspeed_plus(cdev->gadget)) { struct usb_ssp_cap_descriptor *ssp_cap; + u8 ssac = 1; + u8 ssic; + int i; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x2) + ssac = 3; + + /* + * Paired RX and TX sublink speed attributes share + * the same SSID. + */ + ssic = (ssac + 1) / 2 - 1; ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength); bos->bNumDeviceCaps++; - /* - * Report typical values. - */ - - le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1)); - ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1); + le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac)); + ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac); ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE; ssp_cap->bReserved = 0; ssp_cap->wReserved = 0; - /* SSAC = 1 (2 attributes) */ - ssp_cap->bmAttributes = cpu_to_le32(1); + ssp_cap->bmAttributes = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic)); - /* Min RX/TX Lane Count = 1 */ ssp_cap->wFunctionalitySupport = - cpu_to_le16((1 << 8) | (1 << 12)); + cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) | + FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) | + FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1)); /* - * bmSublinkSpeedAttr[0]: - * ST = Symmetric, RX - * LSE = 3 (Gbps) - * LP = 1 (SuperSpeedPlus) - * LSM = 10 (10 Gbps) + * Use 1 SSID if the gadget supports up to gen2x1 or not + * specified: + * - SSID 0 for symmetric RX/TX sublink speed of 10 Gbps. + * + * Use 1 SSID if the gadget supports up to gen1x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * + * Use 2 SSIDs if the gadget supports up to gen2x2: + * - SSID 0 for symmetric RX/TX sublink speed of 5 Gbps. + * - SSID 1 for symmetric RX/TX sublink speed of 10 Gbps. */ - ssp_cap->bmSublinkSpeedAttr[0] = - cpu_to_le32((3 << 4) | (1 << 14) | (0xa << 16)); - /* - * bmSublinkSpeedAttr[1] = - * ST = Symmetric, TX - * LSE = 3 (Gbps) - * LP = 1 (SuperSpeedPlus) - * LSM = 10 (10 Gbps) - */ - ssp_cap->bmSublinkSpeedAttr[1] = - cpu_to_le32((3 << 4) | (1 << 14) | - (0xa << 16) | (1 << 7)); + for (i = 0; i < ssac + 1; i++) { + u8 ssid; + u8 mantissa; + u8 type; + + ssid = i >> 1; + + if (cdev->gadget->max_ssp_rate == USB_SSP_GEN_2x1 || + cdev->gadget->max_ssp_rate == USB_SSP_GEN_UNKNOWN) + mantissa = 10; + else + mantissa = 5 << ssid; + + if (i % 2) + type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX; + else + type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX; + + ssp_cap->bmSublinkSpeedAttr[i] = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, + USB_SSP_SUBLINK_SPEED_LSE_GBPS) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, + USB_SSP_SUBLINK_SPEED_LP_SSP) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, mantissa)); + } } return le16_to_cpu(bos->wTotalLength); @@ -948,9 +890,9 @@ result = 0; } - INFO(cdev, "%s config #%d: %s\n", - usb_speed_string(gadget->speed), - number, c ? c->label : "unconfigured"); + DBG(cdev, "%s config #%d: %s\n", + usb_speed_string(gadget->speed), + number, c ? c->label : "unconfigured"); if (!c) goto done; @@ -1357,7 +1299,7 @@ EXPORT_SYMBOL_GPL(usb_string_id); /** - * usb_string_ids() - allocate unused string IDs in batch + * usb_string_ids_tab() - allocate unused string IDs in batch * @cdev: the device whose string descriptor IDs are being allocated * @str: an array of usb_string objects to assign numbers to * Context: single threaded during gadget setup @@ -1821,7 +1763,7 @@ if (!gadget_is_dualspeed(gadget) || gadget->speed >= USB_SPEED_SUPER) break; - /* FALLTHROUGH */ + fallthrough; case USB_DT_CONFIG: value = config_desc(cdev, w_value); if (value >= 0) @@ -2163,7 +2105,7 @@ return value; } -void composite_disconnect(struct usb_gadget *gadget) +static void __composite_disconnect(struct usb_gadget *gadget) { struct usb_composite_dev *cdev = get_gadget_data(gadget); unsigned long flags; @@ -2178,6 +2120,23 @@ if (cdev->driver->disconnect) cdev->driver->disconnect(cdev); spin_unlock_irqrestore(&cdev->lock, flags); +} + +void composite_disconnect(struct usb_gadget *gadget) +{ + usb_gadget_vbus_draw(gadget, 0); + __composite_disconnect(gadget); +} + +void composite_reset(struct usb_gadget *gadget) +{ + /* + * Section 1.4.13 Standard Downstream Port of the USB battery charging + * specification v1.2 states that a device connected on a SDP shall only + * draw at max 100mA while in a connected, but unconfigured state. + */ + usb_gadget_vbus_draw(gadget, 100); + __composite_disconnect(gadget); } /*-------------------------------------------------------------------------*/ @@ -2488,6 +2447,10 @@ usb_gadget_clear_selfpowered(gadget); usb_gadget_vbus_draw(gadget, maxpower); + } else { + maxpower = CONFIG_USB_GADGET_VBUS_DRAW; + maxpower = min(maxpower, 100U); + usb_gadget_vbus_draw(gadget, maxpower); } cdev->suspended = 0; @@ -2500,7 +2463,7 @@ .unbind = composite_unbind, .setup = composite_setup, - .reset = composite_disconnect, + .reset = composite_reset, .disconnect = composite_disconnect, .suspend = composite_suspend, -- Gitblit v1.6.2