diff options
| author | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
|---|---|---|
| committer | Dmitry Torokhov <[email protected]> | 2023-08-30 16:06:38 -0700 | 
| commit | 1ac731c529cd4d6adbce134754b51ff7d822b145 (patch) | |
| tree | 143ab3f35ca5f3b69f583c84e6964b17139c2ec1 /drivers/usb/gadget/composite.c | |
| parent | 07b4c950f27bef0362dc6ad7ee713aab61d58149 (diff) | |
| parent | 54116d442e001e1b6bd482122043b1870998a1f3 (diff) | |
Merge branch 'next' into for-linus
Prepare input updates for 6.6 merge window.
Diffstat (limited to 'drivers/usb/gadget/composite.c')
| -rw-r--r-- | drivers/usb/gadget/composite.c | 134 | 
1 files changed, 125 insertions, 9 deletions
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index fa7dd6cf014d..1b3489149e5e 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -492,6 +492,46 @@ int usb_interface_id(struct usb_configuration *config,  }  EXPORT_SYMBOL_GPL(usb_interface_id); +/** + * usb_func_wakeup - sends function wake notification to the host. + * @func: function that sends the remote wakeup notification. + * + * Applicable to devices operating at enhanced superspeed when usb + * functions are put in function suspend state and armed for function + * remote wakeup. On completion, function wake notification is sent. If + * the device is in low power state it tries to bring the device to active + * state before sending the wake notification. Since it is a synchronous + * call, caller must take care of not calling it in interrupt context. + * For devices operating at lower speeds  returns negative errno. + * + * Returns zero on success, else negative errno. + */ +int usb_func_wakeup(struct usb_function *func) +{ +	struct usb_gadget	*gadget = func->config->cdev->gadget; +	int			id; + +	if (!gadget->ops->func_wakeup) +		return -EOPNOTSUPP; + +	if (!func->func_wakeup_armed) { +		ERROR(func->config->cdev, "not armed for func remote wakeup\n"); +		return -EINVAL; +	} + +	for (id = 0; id < MAX_CONFIG_INTERFACES; id++) +		if (func->config->interface[id] == func) +			break; + +	if (id == MAX_CONFIG_INTERFACES) { +		ERROR(func->config->cdev, "Invalid function\n"); +		return -EINVAL; +	} + +	return gadget->ops->func_wakeup(gadget, id); +} +EXPORT_SYMBOL_GPL(usb_func_wakeup); +  static u8 encode_bMaxPower(enum usb_device_speed speed,  		struct usb_configuration *c)  { @@ -513,6 +553,19 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,  		return min(val, 900U) / 8;  } +void check_remote_wakeup_config(struct usb_gadget *g, +				struct usb_configuration *c) +{ +	if (USB_CONFIG_ATT_WAKEUP & c->bmAttributes) { +		/* Reset the rw bit if gadget is not capable of it */ +		if (!g->wakeup_capable && g->ops->set_remote_wakeup) { +			WARN(c->cdev, "Clearing wakeup bit for config c.%d\n", +			     c->bConfigurationValue); +			c->bmAttributes &= ~USB_CONFIG_ATT_WAKEUP; +		} +	} +} +  static int config_buf(struct usb_configuration *config,  		enum usb_device_speed speed, void *buf, u8 type)  { @@ -888,6 +941,9 @@ static void reset_config(struct usb_composite_dev *cdev)  		if (f->disable)  			f->disable(f); +		/* Section 9.1.1.6, disable remote wakeup when device is reset */ +		f->func_wakeup_armed = false; +  		bitmap_zero(f->endpoints, 32);  	}  	cdev->config = NULL; @@ -994,6 +1050,11 @@ static int set_config(struct usb_composite_dev *cdev,  		power = min(power, 500U);  	else  		power = min(power, 900U); + +	if (USB_CONFIG_ATT_WAKEUP & c->bmAttributes) +		usb_gadget_set_remote_wakeup(gadget, 1); +	else +		usb_gadget_set_remote_wakeup(gadget, 0);  done:  	if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)  		usb_gadget_set_selfpowered(gadget); @@ -1948,9 +2009,20 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)  		f = cdev->config->interface[intf];  		if (!f)  			break; -		status = f->get_status ? f->get_status(f) : 0; -		if (status < 0) -			break; + +		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; +			} +		} +  		put_unaligned_le16(status & 0x0000ffff, req->buf);  		break;  	/* @@ -1972,8 +2044,44 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)  			if (!f)  				break;  			value = 0; -			if (f->func_suspend) +			if (f->func_suspend) {  				value = f->func_suspend(f, w_index >> 8); +			/* SetFeature(FUNCTION_SUSPEND) */ +			} else if (ctrl->bRequest == USB_REQ_SET_FEATURE) { +				if (!(f->config->bmAttributes & +				      USB_CONFIG_ATT_WAKEUP) && +				     (w_index & USB_INTRF_FUNC_SUSPEND_RW)) +					break; + +				f->func_wakeup_armed = !!(w_index & +							  USB_INTRF_FUNC_SUSPEND_RW); + +				if (w_index & USB_INTRF_FUNC_SUSPEND_LP) { +					if (f->suspend && !f->func_suspended) { +						f->suspend(f); +						f->func_suspended = true; +					} +				/* +				 * Handle cases where host sends function resume +				 * through SetFeature(FUNCTION_SUSPEND) but low power +				 * bit reset +				 */ +				} else { +					if (f->resume && f->func_suspended) { +						f->resume(f); +						f->func_suspended = false; +					} +				} +			/* ClearFeature(FUNCTION_SUSPEND) */ +			} else if (ctrl->bRequest == USB_REQ_CLEAR_FEATURE) { +				f->func_wakeup_armed = false; + +				if (f->resume && f->func_suspended) { +					f->resume(f); +					f->func_suspended = false; +				} +			} +  			if (value < 0) {  				ERROR(cdev,  				      "func_suspend() returned error %d\n", @@ -2079,10 +2187,9 @@ unknown:  				sizeof(url_descriptor->URL)  				- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset); -			if (ctrl->wLength < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH -					    + landing_page_length) -				landing_page_length = ctrl->wLength -					- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset; +			if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length) +				landing_page_length = w_length +				- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;  			memcpy(url_descriptor->URL,  				cdev->landing_page + landing_page_offset, @@ -2516,7 +2623,12 @@ void composite_resume(struct usb_gadget *gadget)  		cdev->driver->resume(cdev);  	if (cdev->config) {  		list_for_each_entry(f, &cdev->config->functions, list) { -			if (f->resume) +			/* +			 * Check for func_suspended flag to see if the function is +			 * in USB3 FUNCTION_SUSPEND state. In this case resume is +			 * done via FUNCTION_SUSPEND feature selector. +			 */ +			if (f->resume && !f->func_suspended)  				f->resume(f);  		} @@ -2531,6 +2643,10 @@ void composite_resume(struct usb_gadget *gadget)  			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;  |